为什么递归模板的 decltype return 类型失败,而 return 类型推导工作正常?
Why does decltype return type fail for recursive template, while return type deduction works just fine?
在处理 C++11 类型集时,我尝试实现此功能(精简到最低限度):
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test() -> decltype(test<Rest...>())
{
return {};
}
gcc 和 clang 都对此感到窒息。铿锵声说:
test.cpp:54:40: error: 'Rest' does not refer to a value
constexpr auto test() -> decltype(test<Rest...>())
^
gcc 抱怨:
test.cpp:54:44: error: expected primary-expression before ‘...’ token
constexpr auto test() -> decltype(test<Rest...>())
我想这是因为 decltype
查看时甚至没有完全声明可变版本的测试。
然而,当我在 C++14 中使用 return 类型推导时,编译得很好:
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test()
{
return test<Rest...>();
}
似乎 test
在这里被认为已充分声明。
我想知道为什么这不适用于 decltype
变体?即使我打开 C++14 支持?
PS:事实证明,我什至不能真正调用 C++14 函数,所以也许整个事情都搞砸了……
一个问题是您的第一个 test
重载不是函数模板,因此无法使用 test<>()
语法调用。
然而,decltype
并不真正适用于像这样的递归可变参数函数,因为 return 类型是函数声明的一部分,因此当名称时不会声明重载被查了。
您可以在 C++11 中使用模板 类 来解决这个问题:
template <typename... Ts>
struct test;
template <typename T, typename... Ts>
struct test<T,Ts...> {
static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }
};
template <>
struct test<> {
static bool value() { return true; }
};
在 C++14 中,您可以使第一个重载采用单个模板参数,而第二个采用两个模板参数和一个参数包:
template <typename T>
constexpr auto test() -> bool { return true; }
template <typename T, typename U, typename... Rest>
constexpr auto test()
{
return test<U,Rest...>();
}
虽然正在声明模板函数本身,但尚未声明模板函数本身。所以它对尾随 return 类型 decltype 不可见。
您可以使用 ADL 解决此问题。如果您的模板函数从与您的模板函数相同的命名空间中获取参数,则 return 类型的查找将变得愿意查看模板函数本身。这是因为模板在声明它们之前使用上下文查找它们的 return 类型签名, 和 在每个参数上使用 ADL。
template<class...Ts> struct types {};
namespace bob{
struct mytag{};
constexpr auto test(mytag, types<>) -> bool{ return true; }
template <typename T, typename... Rest>
constexpr auto test(mytag, types<T,Rest...>)
-> decltype( test( mytag{}, types<Rest...>{} ) )
{
return test(mytag{},types<Rest...>{});
}
}
template<class...Ts>
constexpr auto test()
->decltype( bob::test( bob::mytag{}, types<Ts...>{} ) ){
return bob::test( bob::mytag{}, types<Ts...>{} );
}
根据编译器的不同,您可能需要 constexpr types(){};
和 mytag
的类似内容。
这也修复了 test<>
在您的原始代码中是非法的这一事实。根据我的经验,函数对通过(标记模板包装)值传递的类型做得更好。重载更友好。
在处理 C++11 类型集时,我尝试实现此功能(精简到最低限度):
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test() -> decltype(test<Rest...>())
{
return {};
}
gcc 和 clang 都对此感到窒息。铿锵声说:
test.cpp:54:40: error: 'Rest' does not refer to a value
constexpr auto test() -> decltype(test<Rest...>())
^
gcc 抱怨:
test.cpp:54:44: error: expected primary-expression before ‘...’ token
constexpr auto test() -> decltype(test<Rest...>())
我想这是因为 decltype
查看时甚至没有完全声明可变版本的测试。
然而,当我在 C++14 中使用 return 类型推导时,编译得很好:
constexpr auto test() -> bool;
template <typename T, typename... Rest>
constexpr auto test()
{
return test<Rest...>();
}
似乎 test
在这里被认为已充分声明。
我想知道为什么这不适用于 decltype
变体?即使我打开 C++14 支持?
PS:事实证明,我什至不能真正调用 C++14 函数,所以也许整个事情都搞砸了……
一个问题是您的第一个 test
重载不是函数模板,因此无法使用 test<>()
语法调用。
然而,decltype
并不真正适用于像这样的递归可变参数函数,因为 return 类型是函数声明的一部分,因此当名称时不会声明重载被查了。
您可以在 C++11 中使用模板 类 来解决这个问题:
template <typename... Ts>
struct test;
template <typename T, typename... Ts>
struct test<T,Ts...> {
static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }
};
template <>
struct test<> {
static bool value() { return true; }
};
在 C++14 中,您可以使第一个重载采用单个模板参数,而第二个采用两个模板参数和一个参数包:
template <typename T>
constexpr auto test() -> bool { return true; }
template <typename T, typename U, typename... Rest>
constexpr auto test()
{
return test<U,Rest...>();
}
虽然正在声明模板函数本身,但尚未声明模板函数本身。所以它对尾随 return 类型 decltype 不可见。
您可以使用 ADL 解决此问题。如果您的模板函数从与您的模板函数相同的命名空间中获取参数,则 return 类型的查找将变得愿意查看模板函数本身。这是因为模板在声明它们之前使用上下文查找它们的 return 类型签名, 和 在每个参数上使用 ADL。
template<class...Ts> struct types {};
namespace bob{
struct mytag{};
constexpr auto test(mytag, types<>) -> bool{ return true; }
template <typename T, typename... Rest>
constexpr auto test(mytag, types<T,Rest...>)
-> decltype( test( mytag{}, types<Rest...>{} ) )
{
return test(mytag{},types<Rest...>{});
}
}
template<class...Ts>
constexpr auto test()
->decltype( bob::test( bob::mytag{}, types<Ts...>{} ) ){
return bob::test( bob::mytag{}, types<Ts...>{} );
}
根据编译器的不同,您可能需要 constexpr types(){};
和 mytag
的类似内容。
这也修复了 test<>
在您的原始代码中是非法的这一事实。根据我的经验,函数对通过(标记模板包装)值传递的类型做得更好。重载更友好。