最简单的"轻量级分类成语"的实现?

Ros*_*ina 6 c++ c++11

我的目标是实现一个谓词,该谓词检测嵌套using别名(或typedef)的存在,该别名充当轻量级标签以指示类具有某些属性(用于通用编程).例如,has_my_tag<T>谓词的行为应如下所示:

struct A {
  using my_tag = void;
};

struct B {};

int main()
{
    static_assert(has_my_tag<A>::value, "");  // evaluate to true if my_tag=void is present
    static_assert(!has_my_tag<B>::value, ""); // false otherwise
}
Run Code Online (Sandbox Code Playgroud)

用户@JoelFalcou将此称为"轻量级分类习惯用法",并在此答案中提供了解决方案.我一直无法找到任何关于该名称的成语的引用(你知道吗?)这是Joel的实现has_my_tag<>:

template<class T, class R = void>  
struct enable_if_type { typedef R type; };

template<class T, class Enable = void>
struct has_my_tag : std::false_type {};

template<class T>
struct has_my_tag<T, typename enable_if_type<typename T::my_tag>::type> : 
std::true_type
{};
Run Code Online (Sandbox Code Playgroud)

这是Compiler Explorer上的一个工作版本:https://godbolt.org/z/EEOBb-

我想出了以下简化版本:

template<class T, class Enable = void>
struct has_my_tag : std::false_type {};

template<class T>
struct has_my_tag<T, typename T::my_tag> : std::true_type
{};
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/yhkHp7

我的问题:简化版是否是实现成语的可接受方式?是否存在失败的情况?是否有一个更简单的版本适用于C++ 11?我更喜欢哪个版本?

根据我的理解,Joel的版本允许my_tag别名任何类型,而我的版本需要my_tag别名void.但鉴于为轻量级谓词测试标记类型的目标,我不清楚哪个版本是首选.

辅助问题:此外,这个成语还有其他名称吗?它是否在我可以调查的任何库中使用?到目前为止,我还没有找到能够显示任何搜索结果的名称.

rma*_*son 7

对于您的设置,原始版本与您的版本没有区别.两者都使用SFINAE来选择正确的has_my_tag.你的版本没有限制但是你typedef/usingmy_tag=void.如果my_tag被typedef为任何其他类型,您的专业不匹配,你最终会实例化了主模板可以看出这里.

原因是你在main中实例化模板,static_assert(has_my_tag<A>::value, "");你没有指定第二个参数,所以使用了默认值(void),即has_my_tag<A,void>::value

您的专业化必须与此相匹配才能考虑.

的使用enable_if_type,(基本上做的工作void_t在C++ 17)是在上启用SFINAE ::type的T成员,但后来总是导致无效的,这样你的专业化将匹配时::type存在的类型,无论my_tag的typedef .

这让你只关心它是否存在,而不是它的类型;

就个人而言,我会使用不依赖于my_typetypedef'd为void的方法,无论是enable_if_type版本,还是类似......

#include <iostream>
#include <type_traits>

struct A {
  using my_tag = void;
};

struct B {};

struct C {
  using my_tag = int; // with void_t also works with my_tag = int
};

struct D {
  struct my_tag{}; //or some struct as the tag
};

// same as your enable_if_type
template <typename...>
using void_t = void;


template<class T, class Enable = void>
struct has_my_tag : std::false_type {};

template<class T>
struct has_my_tag<T, void_t<typename T::my_tag>> : std::true_type
{};


int main() {
    std::cout << has_my_tag<A>::value << std::endl;
    std::cout << has_my_tag<B>::value << std::endl;
    std::cout << has_my_tag<C>::value << std::endl;
    std::cout << has_my_tag<D>::value << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

演示

  • 我必须说,这里(以及在OP中)专业化交互的方式可能不直观。如果我理解正确的话,模板的实例化将通过“ false_type”版本的模板签名来推导“ has_my_tag &lt;T,void&gt;”,然后将“ T”替换为两个版本,最后匹配“ true_type”版本,因为版本更多专门。我说对了吗? (2认同)
  • 是的,听起来您做对了。只是当实例化has_my_tag &lt;T&gt;时,它使用默认值,即has_my_tag &lt;T,void&gt;。默认值仅在实例化时相关。当考虑模板和特殊化时,可以忽略默认值,而剩下的是&lt;class T,Class Enable&gt;类(主模板)和特殊化&lt;class T,void&gt;。&lt;T,void&gt;显然更适合于这些类型,因此选择了-像https://ideone.com/LA6Plr一样-使用c ++使实际上如此简单的内容看起来如此直观和复杂,真是令人遗憾。 (2认同)