依赖类型不明确的 c++11 可变参数函数模板重载是否不明确
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 按预期编译和运行,但使用 clang 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...
? - 与标准相比,clang是否正确,还是这是一个错误?第二个例子真的比第一个更模棱两可吗?(对于第一个示例,似乎您可以说单个参数重载与第二个参数重载不明确,第二个参数用
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);
}
相关文章:
- 为函数定义符号不明确的指针参数
- 父类的私有函数会导致对具有相同名称和相似参数的子类中的公共函数的不明确调用
- 对函数的重新定义和不明确的调用具有数组参数
- 函数模板和不明确的模板参数
- 编译器确定不明确的参数
- 重载调用是不明确的:一对内联映射作为构造函数参数
- 如果存在具有不同参数的继承成员,为什么对 C++ 结构函数的调用不明确?
- C++具有可变参数包的函数的部分模板参数推导会在 Clang 和 MSVC 中产生不明确的调用
- 不明确的可变参数类成员访问
- 不明确的重载函数仅在参数的模板参数上有所不同
- 递归调用可变参数模板函数重载时的不明确调用
- 对没有参数的可变参数模板函数的不明确调用
- 解决模板参数推导中的不明确调用
- 可变函数指针参数的模板参数推导-处理不明确的情况
- 64 位C++传递具有"不同"调用约定作为参数的函数会产生不明确的错误
- SFINAE:如果在没有参数的情况下调用,则重载不明确
- 模板参数推导,案例不明确
- 使用可变模板参数传递成员函数指针是不明确的
- 错误 C2782:"常量_Ty &std::min(常量 _Ty &,常量_Ty &)":模板参数"_Ty"不明确
- 依赖类型不明确的 c++11 可变参数函数模板重载是否不明确