[英]Is a c++11 variadic function template overload with a dependent type ambiguous?
以下代码是递归可变参数函数重载的教科书示例。 在clang和GCC中,它编译干净, main
返回36(如预期的那样):
template <typename T>
int add(T val)
{
return val;
}
template <typename FirstTypeT, typename... RestT>
int add(FirstTypeT first_value, RestT... rest)
{
return first_value + add<RestT...>(rest...);
}
int main(void)
{
return add(12, 12, 12);
}
但是,这里稍作修改。 它直接在模板定义中使用依赖类型而不是模板参数:
struct Foo
{
using SomeType = int;
};
template <typename T>
int add(typename T::SomeType val)
{
return val;
}
template <typename FirstT, typename... RestT>
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest)
{
return first_value + add<RestT...>(rest...);
}
int main(void)
{
return add<Foo, Foo, Foo>(12, 12, 12);
}
它编译和运行为使用GCC 5.2预期,但未能使用铛3.8:
clang++ variadic.cpp -o var -std=c++11 -Wall
variadic.cpp:15:26: error: call to 'add' is ambiguous
return first_value + add<RestT...>(rest...);
^~~~~~~~~~~~~
variadic.cpp:15:26: note: in instantiation of function template specialization 'add<Foo, Foo>' requested here
return first_value + add<RestT...>(rest...);
^
variadic.cpp:20:12: note: in instantiation of function template specialization 'add<Foo, Foo, Foo>' requested here
return add<Foo, Foo, Foo>(12, 12, 12);
^
variadic.cpp:7:5: note: candidate function [with T = Foo]
int add(typename T::SomeType val)
^
variadic.cpp:13:5: note: candidate function [with FirstT = Foo, RestT = <>]
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest)
^
1 error generated.
我的问题是双重的。
typename RestT::SomeType...
? RestT = <>
实例化的RestT = <>
) 目前的措辞非常清楚:在部分排序期间完全忽略参数包,因为它没有参数( [temp.deduct.partial] /(3.1) )。 [temp.func.order] / 5也提供了一个非常简单的例子,即使使用可推导的模板参数 - 表明你的第一个例子也是不明确的:
[ 注意 :由于调用上下文中的部分排序仅考虑具有显式调用参数的参数,因此会忽略某些参数(即函数参数包,具有默认参数的参数和省略号参数)。 [...] [ 示例 :
template<class T, class... U> void f(T, U ...); // #1 template<class T > void f(T ); // #2 void h(int i) { f(&i); // error: ambiguous // [...] }
但是,这不是最佳的。 可变参数模板部分排序存在核心问题1395 :
CWG同意应该接受该示例,将此案例作为后期决胜局处理, 更喜欢在参数包上省略参数 。
( 问题1825提供了更精确的策略。)两个编译器都针对第一种情况实施此规则; 只有GCC会为第二个做(即可以考虑提前半步)。
错误消息已经显示原因。
生成add(12)时 ,有两个可用的模板函数。 那是
template <typename T>
int add(typename T::SomeType val);
和
template <typename FirstT, typename... RestT>
int add(typename FirstT::SomeType first_value, typename RestT::SomeType... rest);
// and RestT is empty here(RestT = <>)
这不是标准用法,而且铿锵是正确的。
考虑这段代码。
#include <tuple>
#include <type_traits>
struct Foo
{
using SomeType = int;
};
// helper function to sum a tuple of any size
template<typename Tuple, std::size_t N>
struct TupleSum {
typedef typename std::tuple_element<N - 1, Tuple>::type ref_t;
typedef typename std::remove_reference<ref_t>::type noref_t;
static noref_t sum(const Tuple& t)
{
return std::get<N - 1>(t) + TupleSum<Tuple, N - 1>::sum(t);
}
};
template<typename Tuple>
struct TupleSum<Tuple, 1> {
typedef typename std::tuple_element<0, Tuple>::type ref_t;
typedef typename std::remove_reference<ref_t>::type noref_t;
static noref_t sum(const Tuple& t)
{
return std::get<0>(t);
}
};
template <typename... RestT>
int add(typename RestT::SomeType... rest) {
typedef decltype(std::forward_as_tuple(rest...)) tuple_t;
return TupleSum<tuple_t, sizeof...(RestT) >::sum(std::forward_as_tuple(rest...));
}
int main(void)
{
return add<Foo, Foo, Foo>(12, 12, 12);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.