我尝试使用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;
有谁知道如何修复它?
答案 0 :(得分:4)
条件表达式中的两种类型都必须有效,这不是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()); };
答案 1 :(得分:2)
当您要求::type
时,编译器需要实例化整个事物。有了整数,就像要求编译器给你那种类型:
std::conditional_t<true, int, decltype(std::declval<int>().foo())>
但不幸的是,这是形成不良的。您需要让编译器做一些选择正确的类型:
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;
这种说法的优点在于它不仅仅适用于基础知识,但如果编译器在类型中找不到.foo()
函数,编译器将选择第一个版本。你甚至可以添加第三个版本:
template<typename T>
struct A<T, void_t<decltype(std::declval<T>().bar())>> {
using type = decltype(std::declval<T>().bar());
};
现在,您的结构也适用于具有.bar()
的类型。
答案 2 :(得分:1)
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>;
我认为在使用点比使用替代品更清楚。