使用clang ++和g ++的SFINAE和CRTP有些神奇

Con*_*tor 6 c++ templates crtp sfinae 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;
}
Run Code Online (Sandbox Code Playgroud)

产量

编译器及其标志

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

更先进的研究

问题

  • 这种编译器标准的行为是否合规?如果是的话,背后有什么理由呢?
  • 可能会导致使用中观察到的行为的差C++ 14和C++ 17之间什么不同铛++编译器时标有线路*被注释(输出是false-std=c++14编译器标志和true与所述-std=c++17一个)?

SJL*_*SJL 3

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

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

因为基类和派生类都是空的(在第一个示例中),所以编译器认为它们是完整的。我想说这是不正确的,但我不是一个期望,也不能确定。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;
    }
};
Run Code Online (Sandbox Code Playgroud)

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