Я пытаюсь создать функцию variadi c, которая принимает любое количество аргументов, но я бы хотел специализировать случай, когда передаются только два аргумента с итераторами. Передача двух аргументов не итераторов, все равно следует использовать версию c variadi c. Я поражаюсь static_assert
неудачей, которую мне не удалось преодолеть. Похоже, что он пытается вычислить все выражение в with_iterator_args
, что завершается неудачей, если функция имеет менее двух аргументов, вместо того, чтобы пропустить оценку остатка, когда проверка для 2 аргументов уже привела к ложному.
Есть ли способ сделать это без добавления еще двух перегрузок для случая с одним и двумя аргументами?
Это то, что у меня есть:
#include <iostream>
#include <vector>
#include <tuple>
// inspired by https://stackoverflow.com/a/7943765/2129246
template <typename... Args>
struct args_traits
{
enum { arity = sizeof...(Args) };
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};
// based on: https://stackoverflow.com/a/30766365/2129246
template <typename T>
struct is_iterator
{
static char test(...);
template <typename U,
typename=typename std::iterator_traits<U>::difference_type,
typename=typename std::iterator_traits<U>::pointer,
typename=typename std::iterator_traits<U>::reference,
typename=typename std::iterator_traits<U>::value_type,
typename=typename std::iterator_traits<U>::iterator_category
> static long test(U&&);
constexpr static bool value = std::is_same<decltype(test(std::declval<T>())),long>::value;
};
template<typename Arg1, typename Arg2>
struct is_iterator_args
{
constexpr static bool value = is_iterator<Arg1>::value && is_iterator<Arg2>::value;
};
template<typename... Args>
struct with_iterator_args
{
constexpr static bool value = args_traits<Args...>::arity == 2
&& is_iterator_args<typename args_traits<Args...>::template arg<0>::type, typename args_traits<Args...>::template arg<1>::type>::value;
};
template <typename T, typename... Args,
typename = typename std::enable_if<!with_iterator_args<Args...>::value>::type>
void some_func(T first, Args&&... args)
{
std::cout << "func(" << first << ") called with " << sizeof...(args) << " args" << std::endl;
}
template <typename T, typename Begin, typename End,
typename = typename std::enable_if<is_iterator_args<Begin, End>::value>::type>
void some_func(T first, Begin begin, End end)
{
std::cout << "func(" << first << ") called with iterators: " << std::distance(begin, end) << std::endl;
}
int main()
{
std::vector<int> v{1, 2, 3};
some_func(1, v.begin(), v.end()); // special case, using iterators
some_func(1, "arg2", 3, std::string("arg4"));
some_func(1, "arg2");
some_func(1);
some_func(1, "arg2", 3, std::string("arg4"), 5.67);
return 0;
}
Это то, что не получается:
In file included from test.cpp:3:
/usr/include/c++/9/tuple: In instantiation of ‘struct std::tuple_element<0, std::tuple<> >’:
/usr/include/c++/9/tuple:1285:12: required from ‘struct std::tuple_element<1, std::tuple<const char (&)[5]> >’
test.cpp:14:69: required from ‘struct args_traits<const char (&)[5]>::arg<1>’
test.cpp:45:3: required from ‘constexpr const bool with_iterator_args<const char (&)[5]>::value’
test.cpp:49:37: required by substitution of ‘template<class T, class ... Args, class> void some_func(T, Args&& ...) [with T = int; Args = {const char (&)[5]}; <template-parameter-1-3> = <missing>]’
test.cpp:68:21: required from here
/usr/include/c++/9/tuple:1303:25: error: static assertion failed: tuple index is in range
1303 | static_assert(__i < tuple_size<tuple<>>::value,
| ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~