传递初始化器列表时,可变进值模板参数推导失败

Variadic template argument deduction fails when passing initializer lists

本文关键字:参数 失败 初始化 列表      更新时间:2023-10-16

Bar持有std::pair s/std::array s/FooValueAdaptor s的std::vectorFooValueAdaptor隐式地将int转换为bool,再转换为FooValue,这在这个人为的示例中没有什么意义,但在我的应用程序中完全有意义。我实现了一个方便的函数Bar::addEntries,用于一次添加多个条目,但是使用GCC 4.8.0使用两个以上的参数调用它无法进行编译。查看下面的错误信息。

#include <array>
#include <utility>
#include <vector>
enum class FooValue {
    A,
    B,
    C
};
class FooValueAdaptor {
public:
    FooValueAdaptor(bool value)
        : m_value(static_cast<FooValue>(value)) {
    }
    FooValueAdaptor(int value)
        : m_value(static_cast<FooValue>(static_cast<bool>(value))) {
    }
    FooValueAdaptor(FooValue value)
        : m_value(value) {
    }
    operator FooValue() {
        return m_value;
    }
    operator bool() {
        return m_value == FooValue::C;
    }
private:
    FooValue m_value;
};
template<std::size_t nFirst, std::size_t nSecond>
class Bar {
public:
    typedef std::array<FooValueAdaptor, nFirst> First;
    typedef std::array<FooValueAdaptor, nSecond> Second;
    typedef std::pair<First, Second> Entry;
    Bar()
        : m_table() {
    }
    void addEntry(First first, Second second) {
        m_table.push_back(std::make_pair(first, second));
    }
    template <typename... Args>
    void addEntries() {
    }
    template <typename... Args>
    void addEntries(First first, Second second, Args... args) {
        addEntry(first, second);
        addEntries(args...);
    }
private:
    std::vector<Entry> m_table;
};
int main(int argc, char **argv) {
    Bar<2, 1> b;
    b.addEntry({ 0, 0 }, { 0 });
    b.addEntries(
        { 0, 1 }, { 0 },
        { 1, 0 }, { 0 },
        { 1, 1 }, { 1 }
    );
    return 0;
}

编译错误信息:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:74:2: error: no matching function for call to ‘Bar<2ul, 1ul>::addEntries(<brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>, <brace-enclosed initializer list>)’
  );
  ^
test.cpp:74:2: note: candidates are:
test.cpp:53:7: note: template<class ... Args> void Bar<nFirst, nSecond>::addEntries() [with Args = {Args ...}; long unsigned int nFirst = 2ul; long unsigned int nSecond = 1ul]
  void addEntries() {
       ^
test.cpp:53:7: note:   template argument deduction/substitution failed:
test.cpp:74:2: note:   candidate expects 0 arguments, 6 provided
  );
  ^
test.cpp:57:7: note: void Bar<nFirst, nSecond>::addEntries(Bar<nFirst, nSecond>::First, Bar<nFirst, nSecond>::Second, Args ...) [with Args = {}; long unsigned int nFirst = 2ul; long unsigned int nSecond = 1ul; Bar<nFirst, nSecond>::First = std::array<FooValueAdaptor, 2ul>; Bar<nFirst, nSecond>::Second = std::array<FooValueAdaptor, 1ul>]
  void addEntries(First first, Second second, Args... args) {
       ^
test.cpp:57:7: note:   candidate expects 2 arguments, 6 provided

如何帮助编译器进行演绎?

你需要明确地告诉编译器你需要什么:

void addEntries(std::initializer_list<std::pair<First, Second>> il) {
   for( const auto& e : il ) {
      addEntry(e.first,e.second);
   }
}

,并像这样命名:

b.addEntry({{ 0, 0 }}, {{ 0 }});
b.addEntries({
    {{{ 0, 1 }}, {{ 0 }}},
    {{{ 1, 0 }}, {{ 0 }}},
    {{{ 1, 1 }}, {{ 1 }}}
});

请注意大量的花括号,但我认为以上实际上是唯一正确的语法。GCC 4.8和Clang 3.2都接受更少的括号,但是Clang给出了很多警告,上面的修复了这个问题。有些人已经在"修复",但这需要一些时间。