完美转发,无需使用ODR

时间:2017-03-19 18:38:00

标签: c++ c++14 constexpr perfect-forwarding one-definition-rule

考虑以下代码段,就好像写在头文件中一样:

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);

但是这里有一个问题:这种声明我的标签的方式非常方便,但如果我使用其中的任何一个,我需要在其他地方提供一个定义。根本不方便。

根据this conveniently specific answer(强调我的):

  

&#34;对象不会被使用&#34; 可能是唯一可疑的条件。基本上,它要求您不必将变量运行时存在作为符号,这反过来意味着

     
      
  • 你不能将它绑定到参考(=&gt;你不要转发它!)
  •   
  • [...]
  •   

......我确实在泡菜中。

如何在不使用ODR的情况下从参数中筛选出标记,同时完善转发其他参数?

  • A&#34;标签&#34;被定义为具有type typedef。

  • 的东西
  • 每个标记声明都是由宏defineTag(name, ActualType)生成的,所以只要它是自包含的并且不会改变(太多)调用的语法,那么改变它就可以了。 makeFoo

  • 根本不关心ODR的替代方案很好。

  • C ++ 17的内联变量听起来像是救赎,但是我想避免将自己锁定在这个项目的最前沿编译器中。

4 个答案:

答案 0 :(得分:4)

如果您可以更改获取type的方式,则可以避免使用枚举常量更改makeFoo调用,枚举常量将作为不同类型的prvalues进行评估:

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);

答案 1 :(得分:1)

生成标记的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{})

答案 2 :(得分:1)

你可以让你的标签变量模板吗?

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>);

答案 3 :(得分:0)

简单地将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);
}