无 ODR 使用的完美转发
Perfect forwarding without ODR-use
考虑以下代码片段,就像写在头文件中一样:
struct Foo {
// ...
};
template <class... Args>
Foo makeFoo(Args &&... args) {
return {std::forward<Args>(args)...};
}
我可以用一些参数调用 makeFoo
,然后返回一个 Foo
。太好了。
现在我要做的是用标签替换makeFoo
的一些参数,看起来就像这样(仍在头文件中):
constexpr struct tag_type { using type = ActualType; } tag;
应在 makeFoo
中检测到这些标记,并在调用 Foo
的构造函数之前替换实际对象。所以调用看起来像:
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
但这里有一个问题:这种声明我的标签的方式非常方便,但如果我 ODR 使用其中任何一个,我需要在其他地方提供定义。一点都不方便
并且根据 (强调我的):
"the object isn't odr-used" is probably the only questionable condition. Basically, it requires that you don't necessitate the variables runtime existence as a symbol, which in turn implies that
- You don't bind it to a reference (=> you don't forward it!)
- [...]
...我真的很困惑。
如何在不使用 ODR 的情况下从参数中筛选出标签,同时完美转发其他参数?
“标签”被定义为具有 type
typedef 的东西。
每个标签声明都是由一个宏defineTag(name, ActualType)
生成的,所以只要它是独立的并且不会改变(太多)语法打电话给 makeFoo
.
完全不涉及 ODR 的替代解决方案就可以了。
C++17 的内联变量听起来像是救世主,但我想避免在这个项目上因为那个单一问题而将自己锁定在前沿编译器中。
constexpr
函数生成标签怎么样?
constexpr struct tag_type { using type = ActualType; };
constexpr tag_type tag() { return {}; }
用法:
auto myFoo = makeFoo("hi", ::tagBar(), 42, ::tagBaz())
或者,您可以根据标签的类型就地构建标签,而不是使用函数:
constexpr struct tag { using type = ActualType; };
用法:
auto myFoo = makeFoo("hi", ::tagBar{}, 42, ::tagBaz{})
您可以将标签设为变量模板吗?
template <class T>
struct tag_t { using type = T; };
template <class T>
constexpr tag_t<T> tag{};
您将用作:
auto myFoo = makeFoo("hi", ::tag<Bar>, 42, ::tag<Baz>);
如果您可以更改获取 type
的方式,则可以通过使用枚举常量来避免更改 makeFoo
调用,这些常量计算为不同类型的纯右值:
template <typename> struct tag_helper;
enum { tagBar }; template <> struct tag_helper<decltype(tagBar)> { using type = Bar; };
enum { tagBaz }; template <> struct tag_helper<decltype(tagBaz)> { using type = Baz; };
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
这样做的正常方法不是简单地声明 constexpr 标签定义静态吗?
据推测,Foo 的构造函数只对类型感兴趣,因此标签类型的多个模型的存在并不重要。
#include <utility>
struct Foo {
template<class...Args>
Foo(Args&&...args)
{}
// ...
};
template <class... Args>
Foo makeFoo(Args &&... args) {
return {std::forward<Args>(args)...};
}
struct Bar {};
struct Baz {};
static constexpr struct tagBar_type { using type = Bar; } tagBar {};
static constexpr struct tagBaz_type { using type = Baz; } tagBaz {};
int main()
{
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
}
考虑以下代码片段,就像写在头文件中一样:
struct Foo {
// ...
};
template <class... Args>
Foo makeFoo(Args &&... args) {
return {std::forward<Args>(args)...};
}
我可以用一些参数调用 makeFoo
,然后返回一个 Foo
。太好了。
现在我要做的是用标签替换makeFoo
的一些参数,看起来就像这样(仍在头文件中):
constexpr struct tag_type { using type = ActualType; } tag;
应在 makeFoo
中检测到这些标记,并在调用 Foo
的构造函数之前替换实际对象。所以调用看起来像:
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
但这里有一个问题:这种声明我的标签的方式非常方便,但如果我 ODR 使用其中任何一个,我需要在其他地方提供定义。一点都不方便
并且根据
"the object isn't odr-used" is probably the only questionable condition. Basically, it requires that you don't necessitate the variables runtime existence as a symbol, which in turn implies that
- You don't bind it to a reference (=> you don't forward it!)
- [...]
...我真的很困惑。
如何在不使用 ODR 的情况下从参数中筛选出标签,同时完美转发其他参数?
“标签”被定义为具有
type
typedef 的东西。每个标签声明都是由一个宏
defineTag(name, ActualType)
生成的,所以只要它是独立的并且不会改变(太多)语法打电话给makeFoo
.完全不涉及 ODR 的替代解决方案就可以了。
C++17 的内联变量听起来像是救世主,但我想避免在这个项目上因为那个单一问题而将自己锁定在前沿编译器中。
constexpr
函数生成标签怎么样?
constexpr struct tag_type { using type = ActualType; };
constexpr tag_type tag() { return {}; }
用法:
auto myFoo = makeFoo("hi", ::tagBar(), 42, ::tagBaz())
或者,您可以根据标签的类型就地构建标签,而不是使用函数:
constexpr struct tag { using type = ActualType; };
用法:
auto myFoo = makeFoo("hi", ::tagBar{}, 42, ::tagBaz{})
您可以将标签设为变量模板吗?
template <class T>
struct tag_t { using type = T; };
template <class T>
constexpr tag_t<T> tag{};
您将用作:
auto myFoo = makeFoo("hi", ::tag<Bar>, 42, ::tag<Baz>);
如果您可以更改获取 type
的方式,则可以通过使用枚举常量来避免更改 makeFoo
调用,这些常量计算为不同类型的纯右值:
template <typename> struct tag_helper;
enum { tagBar }; template <> struct tag_helper<decltype(tagBar)> { using type = Bar; };
enum { tagBaz }; template <> struct tag_helper<decltype(tagBaz)> { using type = Baz; };
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
这样做的正常方法不是简单地声明 constexpr 标签定义静态吗?
据推测,Foo 的构造函数只对类型感兴趣,因此标签类型的多个模型的存在并不重要。
#include <utility>
struct Foo {
template<class...Args>
Foo(Args&&...args)
{}
// ...
};
template <class... Args>
Foo makeFoo(Args &&... args) {
return {std::forward<Args>(args)...};
}
struct Bar {};
struct Baz {};
static constexpr struct tagBar_type { using type = Bar; } tagBar {};
static constexpr struct tagBaz_type { using type = Baz; } tagBaz {};
int main()
{
auto myFoo = makeFoo("hi", ::tagBar, 42, ::tagBaz);
}