使用`std :: conditional_t`来定义一个类' `typedef`依赖于其模板参数

时间:2016-11-10 14:04:49

标签: c++ templates c++14

我尝试使用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;

有谁知道如何修复它?

3 个答案:

答案 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()); };

demo

答案 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>;

我认为在使用点比使用替代品更清楚。