以下代码提供了带有和不带标记为*
的行的不同输出:
#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
,true
标记为-std=c++17
?答案 0 :(得分: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;
}
};
给derived
成员typedef using return_type = int;
的简单解决方案将无法正常工作,因为在时基尝试访问typedef时,派生不会完成。
答案 1 :(得分:2)
将我的评论转回答案:
我认为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);
如果非依赖名称的含义在定义上下文和模板特化的实例化点之间发生变化,则程序格式错误,无需诊断。在以下情况下可以这样做:
- 非依赖名称中使用的类型在定义时不完整,但在实例化时完成。
magic
似乎就是这种情况。
即使注释了magic
,以下内容也应该使代码生成错误:
static constexpr bool value = is_complete<Derived>{};