我尝试使用std::conditional_t
来定义一个类A
的typedef
依赖于其模板参数T
:
template< typename T >
class A
{
public:
typedef std::conditional_t< std::is_fundamental<T>::value, T, decltype(std::declval<T>().foo())> type;
};
template< typename T >
class B
{
public:
T foo()
{
// ...
}
};
int main()
{
typename A< int >::type a = 5; // causes an error
typename A< B<int> >::type b = 5; // does not cause an error
return 0;
}
不幸的是,代码无法编译。错误:
error: member reference base type 'int' is not a structure or union
typedef std::conditional_t< std::is_fundamental<T>::value, T, decltype(std::declval<T>().foo())> type;
有谁知道如何解决它?
条件表达式中的两种类型都必须有效,这不是 SFINAE 上下文。
您可以通过仅"执行"所选类模板来实现所需的内容:
typedef typename std::conditional_t< std::is_fundamental<T>::value, identity<T>, foo_t<T>>::type type;
这些定义为:
template<typename T>
struct identity{ using type = T; };
template<typename T>
struct foo_t{ using type = decltype(std::declval<T>().foo()); };
演示
当你要求::type
时,编译器需要实例化整个事情。使用 ints,就像要求编译器给你这个类型:
std::conditional_t<true, int, decltype(std::declval<int>().foo())>
但不幸的是,这是不正确的。你需要让编译器做一些sfinae来选择正确的类型:
template<typename T, typename = void>
struct A {
using type = T;
};
template<typename T>
struct A<T, void_t<decltype(std::declval<T>().foo())>> {
using type = decltype(std::declval<T>().foo());
};
如果可能,编译器将选择第二个专用化。换句话说,如果类型T
确实.foo()
可能,则type
将等于该表达式的类型。
您可以实现如下void_t
:
template<typename...>
using void_t = void;
这种 silution 的优点是它不仅适用于基本原理,但如果编译器在该类型中找不到.foo()
函数,它将选择第一个版本。您甚至可以添加第三个版本:
template<typename T>
struct A<T, void_t<decltype(std::declval<T>().bar())>> {
using type = decltype(std::declval<T>().bar());
};
现在,您的结构也适用于具有.bar()
的类型。
template<template<class...>class Z>
struct wrap_z{
template<class...Ts>
using result=Z<Ts...>;
};
template<bool b, template<class...>class T, template<class...>class F, class...Ts>
using conditional_apply =
std::conditional_t<
b, wrap_z<T>, wrap_z<F>
>::template result<Ts...>;
template<class T>using identity = T;
现在我们使用它:
public:
template<class T>using do_foo = decltype(std::declval<T>().foo());
using type = conditional_apply< std::is_fundamental<T>{}, identity, do_foo, T>;
我认为这在使用点比替代方案更清晰。