有人可以向我解释以下模板代码吗?

时间:2019-11-18 03:31:33

标签: c++ templates

我是模板编程的新手,我打算在这里使用该解决方案来确保所使用的类型已定义了运算符。但我想了解这段代码。我查看了cppreference的信息,但对它的工作方式更加困惑。

How to check whether operator== exists?

不幸的是,这对我来说是个神秘的事物,以为会询问代码中某些内容的含义和原因。

namespace CHECK
{
    struct No {};
    template <typename T, typename Arg>
    No operator==(const T&, const Arg&);

    /* Why are there two definitions of structures? Is this needed at all? Is
     * there a simpler version of this whole code snippet?
     */
    template <typename T, typename Arg = T>
    struct EqualExists {
        /* Why does this have to be a enum? What effect does this have? */
        enum {
            /* What happens if == sign is not defined for T? What would the
             * below comparison return when true/false? (The comparison *(T*)(0) == *(Arg*)(0))
             *
             * What is the need for No here? Why is it needed and how will
             * comparing it with the return type of == comparison be true ever?
             * How can the return type of == comparison and No structure be the same?
             */
            value = !std::is_same<decltype(*(T*)(0) == *(Arg*)(0)), No>::value
        };
    };
}

有人可以将其链接到原始问题吗?希望这可以帮助刚接触cpp的人也了解这一点。

1 个答案:

答案 0 :(得分:2)

说明

让我们按步骤进行分解:

  • (T*)(0):假装在地址0处有一个T类型的对象;该子表达式的类型是一个指针
  • *(T*)(0):假装在地址0处有一个T类型的对象;由于前面的星号会取消引用指针,因此该子表达式的类型为引用。看起来有点像是对空指针的取消引用,但稍后会详细介绍。
  • *(Arg*)(0):假装在地址0处有一个Arg类型的对象;与T
  • 相同的模式
  • *(T*)(0) == *(Arg*)(0):这等效于使用letting the compiler figure out where the operator is defined的额外方便来调用operator==<T, Arg>( *(T*)(0), *(Arg*)(0) )
    • 如果用户定义的operator==不存在,则将匹配CHECK名称空间运算符。这是“全部捕获”模板。
  • decltype(*(T*)(0) == *(Arg*)(0))decltype"don't execute the subexpression in the parentheses; just give me its type"。在这种情况下,类型是operator==调用的返回类型。
    • 实际上没有进行比较操作,也没有对内存的取消引用。
  • std::is_same<decltype(*(T*)(0) == *(Arg*)(0)), No>::value:如果类型相同,则std::is_samevaluetrue,否则为false
    • ::value是静态constexpr bool
    • 两个类型参数是decltype(...)和结构CHECK::No
    • 现实世界中的operator==通常返回bool。有时它可能返回用户定义的类型。不太可能有人编写自己的自定义operator==来返回CHECK::No,而此代码正是基于这种假设。
  • enum { value = !std::is_same<...>::value }:枚举始终是一个编译时常量,可在较旧的C ++编译器和规范(如C ++ 03,其中constexpr不存在)上工作,并且与{{ 1}},并且不需要存储。
    • constexpr应该是等效的。

发出示例代码:

  • 从技术上来说取消引用空指针是非法的; std::declval是安全的选择。
  • 从技术上讲有人可以写static constexpr bool value = !std::is_same<...>::value;,这会愚弄检查者以为操作员未定义。
  • CHECK名称空间中的CHECK::No operator==(const Foo&, const Bar&)可能会遮盖全局定义的operator ==定义,从而导致假阴性。

替代实现

要解决上述问题并“简化”,一种方法是使用SFINAE。尽管“简单”在这里是主观的。

[edit]链接到工作版本:http://coliru.stacked-crooked.com/a/e9cc48729d53b6c6,并在下面更新了代码

operator==

[edit]我最初的答案有一个错误:在Exists计算中未使用模板args U和V,并且无法在gcc上编译。 (出于某些原因在msvc上工作)

    template <typename T, typename Arg = T>
    class EqualExists {
        template <class U = T, class V = Arg, bool Exists = !!sizeof(std::declval<U>() == std::declval<V>())>
        static std::true_type Func(const T&, const Arg&);

        template <class U, class V>
        static std::false_type Func(const U&, const V&);

    public:
        static constexpr bool value = decltype(Func(std::declval<T>(), std::declval<Arg>()))::value;
    };