使用"std::conditional_t"定义依赖于其模板参数的类"typedef"



我尝试使用std::conditional_t来定义一个类Atypedef依赖于其模板参数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>;

我认为这在使用点比替代方案更清晰。

最新更新