Я пытаюсь построить шаблон функции, скажем util::caller
, чтобы применить элементы, хранящиеся в std::vector<T>
, к функции, которая принимает эти элементы в качестве аргументов.Например, у меня есть функция int func(int a, int b, int c)
и вектор int std::vector<int> args = {1, 2, 3}
, вызов функции может быть похож на следующий фрагмент кода.
int func(int a, int b, int c) {
return a + b + c;
}
int main() {
std::vector<int> args = {1, 2, 3};
util::caller(func, args);
return 0;
}
Реализация util::caller
почти завершена, чьяподпись выглядит следующим образом:
template <typename FuncType,
typename VecType,
size_t... I,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT caller(FuncType& func,
VecType& args,
indices<I...> placeholder = BuildIndices<Traits::arity>());
Определение материалов, таких как function_traits
и BuildIndices
, содержится в более поздней части этого сообщения.
Компилятор сообщаетнеожиданная ошибка, когда я звоню func
как util::caller(func, args)
, но все нормально, когда я звоню func
как util::caller(func, args, BuildIndices<3>())
.Пожалуйста, обратите внимание, что в случае int func(int, int, int)
, Traits::arity
равняется 3UL
.Другими словами, два вызова util::caller
одинаковы!
Это меня сильно смутило, и я не уверен, является ли это ошибкой компилятора.(gcc, clang, icc, msvc сообщат об этой неожиданной ошибке.) Может кто-нибудь объяснить это?Будем благодарны за любые подсказки или подсказки.
MWE можно найти по https://gcc.godbolt.org/z/JwHk6_ или:
#include <iostream>
#include <utility>
#include <vector>
namespace util {
template <typename ReturnType, typename... Args>
struct function_traits_defs {
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
template <size_t i>
struct arg {
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename T>
struct function_traits_impl;
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&&>
: function_traits_defs<ReturnType, Args...> {};
template <typename T, typename V = void>
struct function_traits
: function_traits_impl<T> {};
template <typename T>
struct function_traits<T, decltype((void)&T::operator())>
: function_traits_impl<decltype(&T::operator())> {};
template <size_t... Indices>
struct indices {
using next = indices<Indices..., sizeof...(Indices)>;
};
template <size_t N>
struct build_indices {
using type = typename build_indices<N - 1>::type::next;
};
template <>
struct build_indices<0> {
using type = indices<>;
};
template <size_t N>
using BuildIndices = typename build_indices<N>::type;
template <typename FuncType,
typename VecType,
size_t... I,
typename Traits = function_traits<FuncType>,
typename ReturnT = typename Traits::result_type>
ReturnT caller(FuncType& func,
VecType& args,
indices<I...> placeholder = BuildIndices<Traits::arity>()) {
return func(args[I]...);
}
template <typename FuncType>
static constexpr size_t arity(FuncType& func) {
return function_traits<FuncType>::arity;
}
} // namespace util
int func(int a, int b, int c) {
return a + b + c;
}
int main() {
std::vector<int> args = {1, 2, 3};
int j = util::caller(func, args); // reports error
// works fine for the following calling
// int j = util::caller(func, args, util::BuildIndices<3>());
// int j = util::caller(func, args, util::BuildIndices<util::arity(func)>());
// int j = util::caller(func, args, util::BuildIndices<util::function_traits<decltype(func)>::arity>());
std::cout << j << std::endl;
return 0;
}
Отчеты об ошибках компилятора:
gcc 9.1:
<source>: In function 'ReturnT util::caller(FuncType&, VecType&, util::indices<I ...>) [with FuncType = int(int, int, int); VecType = std::vector<int>; long unsigned int ...I = {}; Traits = util::function_traits<int(int, int, int), void>; ReturnT = int]':
<source>:116:34: error: could not convert 'util::BuildIndices<3>()' from 'indices<#'nontype_argument_pack' not supported by dump_expr#<expression error>>' to 'indices<#'nontype_argument_pack' not supported by dump_expr#<expression error>>'
116 | int j = util::caller(func, args); // reports error
| ^
| |
| indices<#'nontype_argument_pack' not supported by dump_expr#<expression error>>
<source>:116:34: note: when instantiating default argument for call to 'ReturnT util::caller(FuncType&, VecType&, util::indices<I ...>) [with FuncType = int(int, int, int); VecType = std::vector<int>; long unsigned int ...I = {}; Traits = util::function_traits<int(int, int, int), void>; ReturnT = int]'
<source>: In function 'int main()':
<source>:116:34: error: could not convert 'util::BuildIndices<3>()' from 'indices<#'nontype_argument_pack' not supported by dump_expr#<expression error>>' to 'indices<#'nontype_argument_pack' not supported by dump_expr#<expression error>>'
Compiler returned: 1
clang 8.0.0:
<source>:99:26: error: no viable conversion from 'indices<0UL aka 0, 1UL aka 1, sizeof...(Indices) aka 2>' to 'indices<(no argument), (no argument), (no argument)>'
indices<I...> placeholder = BuildIndices<Traits::arity>()) {
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:116:11: note: in instantiation of default function argument expression for 'caller<int (int, int, int), std::vector<int, std::allocator<int> >, util::function_traits<int (int, int, int), void>, int>' required here
int j = util::caller(func, args); // reports error
^
<source>:78:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'BuildIndices<function_traits<int (int, int, int), void>::arity>' (aka 'indices<0UL, 1UL, sizeof...(Indices)>') to 'const util::indices<> &' for 1st argument
struct indices {
^
<source>:78:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'BuildIndices<function_traits<int (int, int, int), void>::arity>' (aka 'indices<0UL, 1UL, sizeof...(Indices)>') to 'util::indices<> &&' for 1st argument
struct indices {
^
<source>:99:26: note: passing argument to parameter 'placeholder' here
indices<I...> placeholder = BuildIndices<Traits::arity>()) {
^
<source>:100:25: error: too few arguments to function call, expected 3, have 0
return func(args[I]...);
~~~~ ^
<source>:116:17: note: in instantiation of function template specialization 'util::caller<int (int, int, int), std::vector<int, std::allocator<int> >, util::function_traits<int (int, int, int), void>, int>' requested here
int = util::caller(func, args); // reports error
^
2 errors generated.
Compiler returned: 1
icc 19.0.1:
<source>(99): error: no suitable user-defined conversion from "util::BuildIndices<3UL>" to "util::indices<>" exists
indices<I...> placeholder = BuildIndices<Traits::arity>()) {
^
detected during instantiation of "ReturnT util::caller(FuncType &, VecType &, util::indices<I...>) [with FuncType=int (int, int, int), VecType=std::vector<int, std::allocator<int>>, I=<>, Traits=util::function_traits<int (int, int, int), void>, ReturnT=int]" at line 116
<source>(100): error #165: too few arguments in function call
return func(args[I]...);
^
detected during instantiation of "ReturnT util::caller(FuncType &, VecType &, util::indices<I...>) [with FuncType=int (int, int, int), VecType=std::vector<int, std::allocator<int>>, I=<>, Traits=util::function_traits<int (int, int, int), void>, ReturnT=int]" at line 116
compilation aborted for <source> (code 2)
Compiler returned: 2
msvc 19.21:
example.cpp
<source>(99): error C2440: 'default argument': cannot convert from 'util::indices<0,1,2>' to 'util::indices<>'
<source>(99): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Compiler returned: 2