c++ - 使用 clang++ 和 g++ 的 SFINAE 和 CRTP 的一些魔法

标签 c++ templates sfinae crtp incomplete-type

代码

下面的代码给出了不同的输出,带有和不带有 * 注释的行:

#include <iostream>
#include <type_traits>


template <bool>
using bool_void_t = void;

template <typename, typename = void>
struct is_complete : std::false_type
{
};

template <typename T>
struct is_complete<T, bool_void_t<sizeof(T) == sizeof(T)>> : std::true_type
{
};

template <typename Derived>
struct Base
{    
    static constexpr bool value = is_complete<Derived>{};

    // using magic = bool_void_t<value>; // *
};

struct Foo : Base<Foo>
{
};

int main()
{
    std::cout << std::boolalpha << Foo::value << std::endl;
}

输出

编译器及其标志

在这两种情况下,clang++ 5.0.0 都用作编译器,编译器标志为 -std=c++17 -Wall -Wextra -Werror -pedantic-errors.

更高级的研究

  • clang++ 5.0.0(-Wall -Wextra -Werror -pedantic-errors)

                         -std=c++14   -std=c++17
    * is commented       false        true
    * is not commented   false        false
    
  • g++ 7.2.1(-Wall -Wextra -Werror -pedantic-errors)

                         -std=c++14   -std=c++17
    * is commented       true         true
    * is not commented   false        false
    

问题

  • 编译器的这种行为是否符合标准?如果是,其背后的理由是什么?
  • 当用 * 标记的行被注释时,C++14 和 C++17 之间的哪些差异可能导致使用 clang++ 编译器观察到的行为不同(使用 -std=c++14 编译器标志时输出为 false,使用 -std=c++ 时输出为 true 17一)?

最佳答案

使用CRTP时常遇到的一个问题是在实例化基类时,派生类不完整。这意味着您不能在派生类中使用成员类型定义等等。

当您考虑时,这是有道理的:模板类实际上是一种根据给定模板类型生成新类类型的方法,因此直到编译器到达结束 }(在一个近似意义),基类没有完全定义。如果基类没有完全定义,那么派生类显然也不能。

因为基类和派生类都是空的(在第一个例子中),编译器认为它们是完整的。我会说这是不正确的,但我不是期望也不能确定。不过,这里的技巧是您在定义基类时实例化 is_complete 的值。完全定义派生类后,它就完成了。

此外,对于这很重要的示例,请考虑这样的事情:

template <typename>
class crtp_traits;

class derived;

template <>
class crtp_traits<derived>
{
public:
    using return_type = int;
};

template <typename T>
class base
{
public:
    auto get_value() const -> typename crtp_traits<T>::return_type
    {
        return static_cast<T const*>(this)->do_get_value();
    }
};

class derived : public base<derived>
{
public:
    auto do_get_value() const -> int
    {
        return 0;
    }
};

derived 成员 typedef using return_type = int; 的简单解决方案将不起作用,因为 derived 不会在时基尝试访问 typedef 时完成.

关于c++ - 使用 clang++ 和 g++ 的 SFINAE 和 CRTP 的一些魔法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48645716/

相关文章:

c++ - 向 Python 公开 C++ API

c++ - 在可以转换为另一个模板类型的类模板中重载赋值运算符

c++ - 如何链接两种不同类型的节点? (模板类)

c++ - 为什么模板参数中的 enable_if_t 提示重新定义?

c++ - 如何检查下标运算符是否存在?

c++ - GetWindowText 与 char[]

c++ - 使用 find 检查,如果我在字符串中有下划线

c++ - 确保 QTableView 中列的唯一值

c++ - 为什么有时使用基本类型作为基类编译?

C++: `enable_if` 限制支持特定算术运算的类型