繁体   English   中英

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

[英]Some magic with SFINAE and CRTP using clang++ and g++

以下代码给出了带有和没有标记*注释的行的不同输出:

#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 ++编译器观察到的行为的差异,当标记为*的行被注释时(输出为false带有-std=c++14编译器标记和对于-std=c++17true吗?

使用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;
    }
};

using return_type = int;给出derived成员typedef的简单解决方案using return_type = int; 将无法正常工作,因为在尝试访问typedef的时基不会完成派生。

把我的评论写成答案:

我认为is_complete使代码is_complete (无需诊断)......
它的值可能取决于它实例化的位置并破坏ODR。

class A; // A not complete yet
static_assert(!is_complete<A>::value);
class A{}; // Now it is
static_assert(is_complete<A>::value);

来自dependent_name

如果非依赖名称的含义在定义上下文和模板特化的实例化点之间发生变化,则程序格式错误,无需诊断。 在以下情况下可以这样做:

  • 非依赖名称中使用的类型在定义时是不完整的,但在实例化时完成。

这似乎是magic的情况。

即使magic被评论,下面也应该使代码形成错误:

static constexpr bool value = is_complete<Derived>{};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM