当元组元素之一具有默认构造函数而另一个没有时,如何将构造函数参数转发到包含的元组

问题描述 投票:0回答:1

我正在尝试编写一个类,它使用

std::tuple
来存储一堆可能具有不同类型的对象。如果元素都具有接受某些参数的构造函数,我可以正确地从容器的构造函数初始化元组。但是,如果其中一个元素只有默认构造函数而其他元素没有,则我无法构造元组。我收到编译错误。

这是代码:

struct Op1WithArg
{
    Op1WithArg(int){}
};

struct Op2WithArg
{
    Op2WithArg(std::string){}
};

struct OpWithoutArg
{
    OpWithoutArg(){}
};

template <class... Ops>
struct OpsSequence {
    using OpTuple = std::tuple<Ops...>;

    template<typename... Args>
    OpsSequence(Args... args)
    :   mOps{args...}
    {}

    OpTuple mOps;
};

void TestSequenceWithOpWithoutArg()
{
    // This works
    using SequenceType = OpsSequence<OpWithoutArg>;
    SequenceType sequence{};
}

void TestSequenceWithTwoOpsWithArg()
{
    // This works
    using SequenceType = OpsSequence<Op1WithArg, Op2WithArg>;
    SequenceType sequence{1, "Hello"};
}

void TestSequenceWithOneOpWithArgAndOneWithout()
{
    // This fails to compile
    using SequenceType = OpsSequence<Op1WithArg, OpWithoutArg>;
    SequenceType sequence{1,{}};
}

最终函数编译失败如下:

|| [ 50%] Building CXX object CMakeFiles/tsconv_tests.dir/tests/tsconv_tests.cpp.o
|| /home/anders/src/tsconv/tests/tsconv_tests.cpp: In function ‘void tsconv::TestSequenceWithOneOpWithArgAndOneWithout()’:
tests/tsconv_tests.cpp|1014 col 31| error: no matching function for call to ‘tsconv::OpsSequence<tsconv::Op1WithArg, tsconv::OpWithoutArg>::OpsSequence(<brace-enclosed initializer list>)’
||  1014 |     SequenceType sequence{1,{}};
||       |                               ^
tests/tsconv_tests.cpp|989 col 5| note: candidate: ‘tsconv::OpsSequence<Ops>::OpsSequence(Args ...) [with Args = {}; Ops = {tsconv::Op1WithArg, tsconv::OpWithoutArg}]’
||   989 |     OpsSequence(Args... args)
||       |     ^~~~~~~~~~~
tests/tsconv_tests.cpp|989 col 5| note:   candidate expects 0 arguments, 2 provided
tests/tsconv_tests.cpp|985 col 8| note: candidate: ‘constexpr tsconv::OpsSequence<tsconv::Op1WithArg, tsconv::OpWithoutArg>::OpsSequence(const tsconv::OpsSequence<tsconv::Op1WithArg, tsconv::OpWithoutArg>&)’
||   985 | struct OpsSequence {
||       |        ^~~~~~~~~~~
tests/tsconv_tests.cpp|985 col 8| note:   candidate expects 1 argument, 2 provided
tests/tsconv_tests.cpp|985 col 8| note: candidate: ‘constexpr tsconv::OpsSequence<tsconv::Op1WithArg, tsconv::OpWithoutArg>::OpsSequence(tsconv::OpsSequence<tsconv::Op1WithArg, tsconv::OpWithoutArg>&&)’
tests/tsconv_tests.cpp|985 col 8| note:   candidate expects 1 argument, 2 provided
gmake[2]: *** [CMakeFiles/tsconv_tests.dir/build.make|76| CMakeFiles/tsconv_tests.dir/tests/tsconv_tests.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2|83| CMakeFiles/tsconv_tests.dir/all] Error 2
gmake: *** [Makefile|101| all] Error 2

是否可以修改代码来编译失败的函数?

c++ templates template-meta-programming
1个回答
0
投票

不幸的是我们没有得到元组

std::piecewise_construct

我看到的唯一解决方案是仅使用默认构造函数向类添加虚拟类型。当然,这需要您自己修改类型,而您可能无法做到:

// Dummy tag type
struct DefaultConstructTag {};  

struct Op1WithArg
{
    Op1WithArg(int){}
};

struct Op2WithArg
{
    Op2WithArg(std::string){}
};

struct OpWithoutArg
{
    // Add dummy type to default constructor but give it a default value 
    // You may still call it like a default constructor without arguments!
    OpWithoutArg(DefaultConstructTag tag = {}) {} 
};

template <class... Ops>
struct OpsSequence {
    using OpTuple = std::tuple<Ops...>;

    template<typename... Args>
    OpsSequence(Args... args)
    :   mOps{args...}
    {}

    OpTuple mOps;
};

void TestSequenceWithOpWithoutArg()
{
    // This works
    using SequenceType = OpsSequence<OpWithoutArg>;
    SequenceType sequence{};
}

void TestSequenceWithTwoOpsWithArg()
{
    // This works
    using SequenceType = OpsSequence<Op1WithArg, Op2WithArg>;
    SequenceType sequence{1, "Hello"};
}

void TestSequenceWithOneOpWithArgAndOneWithout()
{
    // Now this code should work
    using SequenceType = OpsSequence<Op1WithArg, OpWithoutArg>;
    SequenceType sequence{1, DefaultConstructTag{}};
}

https://godbolt.org/z/Yn8cE8sK7

© www.soinside.com 2019 - 2024. All rights reserved.