定义类型 A 等于类型 B(如果 B 存在)

问题描述 投票:0回答:1

如果

TagOrInt<T>
具有成员类型
T::Tag
,我希望
T
等于
Tag
,否则等于
int
。喜欢:

template <class T> using TagOrint = typename T::Tag; // if this is valid
template <class T> using TagOrInt = int;             // otherwise

我可以使用 SFINAE 和辅助结构来做到这一点:

#include <type_traits>
template<class T, class = void>
struct TagOrIntHelper {
  using type = int;
};
template<class T>
struct TagOrIntHelper<T, std::void_t<typename T::Tag> > {
  using type = T::Tag;
};
template<class T>
using TagOrInt = TagOrIntHelper<T>::type;

// Test case
struct X { using Tag = float; };
struct Y { };
static_assert(std::is_same_v<TagOrInt<X>, float>);
static_assert(std::is_same_v<TagOrInt<Y>, int>);

这可行,但是有没有办法在没有 SFINAE 和辅助结构的情况下做到这一点?我觉得应该有一种使用概念的C++20方式。或者 C++17 中有更简单的方法吗?

这是我对概念的尝试,但它不起作用:

#include <type_traits>
template<class T>
concept HasTag = requires { (typename T::Tag *)nullptr; };
template<class T>
using TagOrInt = std::conditional_t<HasTag<T>, typename T::Tag, int>;

// Test case
struct Y { };
static_assert(std::is_same_v<TagOrInt<Y>, int>);

Tag_or_int<Y>
的实例化无法编译:

concept.cc:5:7: error: no type named ‘Tag’ in ‘struct Y’

所以显然

std::conditional_t
要求两个分支都编译。

c++ templates c++20 sfinae c++-concepts
1个回答
1
投票

你可以这样做:

template <class T>
using TagOrint = decltype([]{
    if constexpr (requires { typename T::Tag; }) {
        return std::type_identity<typename T::Tag>();
    } else {
        return std::type_identity<T>();
    }
}())::type;

基本上 - lambda 为您提供了一个可以

if constexpr
满足该约束的位置。但是 lambda 不能返回 type,它只能返回一个值 - 所以我们返回某种
std::type_identity<T>
。然后我们需要
decltype(...)::type
T
实际拉出。

© www.soinside.com 2019 - 2024. All rights reserved.