保证复制省略和返回类型的前向声明
Guaranteed copy elision and forward declaration of returned type
我想使用 CRTP 为定义了一组函数的所有类型编写一个通用接口。
以下代码无法编译,因为我在定义其返回类型之前调用了一个函数:
// interface.h
struct Obj;
template <typename Derived>
struct Interface
{
// error: return type 'struct Obj' is incomplete
Obj f() const { return static_cast<const Derived&>(*this).f_impl(); }
};
// a.h
struct A : Interface<A>
{
Obj f_impl();
};
// obj.h
struct Obj{};
// a.cpp
#include "a.h"
#include "obj.h"
Obj A::f_impl()
{
return {};
}
使用 C++17 和保证复制省略,我希望编译器在定义 f 时不再需要 Obj 的定义。这可能吗?还是必须在 interface.h 中#include obj.h?第二个选项很烦人,因为它大大增加了编译时间。
即使使用 "guaranteed copy elision"(不幸的是,这有点用词不当),C++ 标准要求 [dcl.fct]/11 函数的 return 类型
[…] shall not be an incomplete (possibly cv-qualified) class type in the context of the function definition unless the function is deleted.
在函数定义中为 return 类型使用占位符类型(正如 Max Langhof 在评论中所建议的那样)应该可以解决这种情况下的问题:
template <typename Derived>
struct Interface
{
auto f() const { return static_cast<const Derived&>(*this).f_impl(); }
};
工作示例here
请注意,"guaranteed copy elision" 确实不能保证副本会被删除,因为它是对语言的更改,这意味着从一开始就不会制作副本。没有副本,也没有什么可以省略的……
不进行复制意味着编译器必须直接在 return 值的目标中构造一个对象。这样做需要一个完整的类型…
我想使用 CRTP 为定义了一组函数的所有类型编写一个通用接口。 以下代码无法编译,因为我在定义其返回类型之前调用了一个函数:
// interface.h
struct Obj;
template <typename Derived>
struct Interface
{
// error: return type 'struct Obj' is incomplete
Obj f() const { return static_cast<const Derived&>(*this).f_impl(); }
};
// a.h
struct A : Interface<A>
{
Obj f_impl();
};
// obj.h
struct Obj{};
// a.cpp
#include "a.h"
#include "obj.h"
Obj A::f_impl()
{
return {};
}
使用 C++17 和保证复制省略,我希望编译器在定义 f 时不再需要 Obj 的定义。这可能吗?还是必须在 interface.h 中#include obj.h?第二个选项很烦人,因为它大大增加了编译时间。
即使使用 "guaranteed copy elision"(不幸的是,这有点用词不当),C++ 标准要求 [dcl.fct]/11 函数的 return 类型
[…] shall not be an incomplete (possibly cv-qualified) class type in the context of the function definition unless the function is deleted.
在函数定义中为 return 类型使用占位符类型(正如 Max Langhof 在评论中所建议的那样)应该可以解决这种情况下的问题:
template <typename Derived>
struct Interface
{
auto f() const { return static_cast<const Derived&>(*this).f_impl(); }
};
工作示例here
请注意,"guaranteed copy elision" 确实不能保证副本会被删除,因为它是对语言的更改,这意味着从一开始就不会制作副本。没有副本,也没有什么可以省略的……
不进行复制意味着编译器必须直接在 return 值的目标中构造一个对象。这样做需要一个完整的类型…