C++ 如何从可变参数包初始化向量?

c++ how to initialize vector from variadic pack?

提问人:Miroslav Krajcir 提问时间:11/5/2023 最后编辑:wohlstadMiroslav Krajcir 更新时间:11/5/2023 访问量:128

问:

我有一个模板化的存储值。s 或 s 没有问题,但值无法编译。编译器似乎假设我想用 s 而不是 values 初始化。我该如何解决这个问题?Containervectorstringintsize_tvectorintsize_t

#include <iostream>
#include <vector>

template <class ValueType>
class Container {
public:
    template <typename... ValueTypes>
    Container(ValueTypes&&... values) :
        currentIndex(0),
        values{ std::forward<ValueTypes>(values)... }
    {}

    const ValueType operator()() const {
        return values[currentIndex];
    }

protected:
    size_t currentIndex;
    std::vector<ValueType> values;
};

int main() {
    Container<std::string> container1("10", "20", "30");    // ok
    Container<int> container2(10, 20, 30);                  // ok
    Container<size_t> container3(10, 20, 30);               // compile error, assumed ints!
}
C++ 矢量 variadic-templates size-t

评论

0赞 Alexander 11/5/2023
我只看到一个警告 godbolt.org/z/GqYsW83q5
0赞 wohlstad 11/5/2023
怎么样 ?Container<size_t> container3(10ULL, 20ULL, 30ULL);
0赞 Alexander 11/5/2023
您可以使用“u”后缀。godbolt.org/z/f7ToxsbsM
0赞 Miroslav Krajcir 11/5/2023
错误 C2398 元素“1”:从“_Ty”到“_Ty”的转换需要缩小转换范围

答:

3赞 wohlstad 11/5/2023 #1

10,并且确实是文字,因此缩小转换错误/警告是意料之中的。2030int

您可以使用(或)后缀使它们符合(无符号):uULLsize_t

Container<size_t> container3(10u, 20u, 30u);

评论

1赞 Miroslav Krajcir 11/5/2023
谢谢。但是std::vector<size_t> v{10, 20, 30}怎么可能没有问题呢?编译器假设此处不带任何后缀size_t...
1赞 user12002570 11/5/2023
@MiroslavKrajcir我已经在这篇文章中非常详细地解释了为什么有效std::vector<size_t> v{10, 20, 30}
1赞 Jesper Juhl 11/5/2023
另一种选择是 - 我同意后缀是更好的选择,但只是提到替代方案。size_t(10)u
4赞 Pepijn Kramer 11/5/2023 #2

这是两种可供选择的方法(第二种方法更类型安全,但需要稍微不同的语法才能使用)。

#include <iostream>
#include <vector>
#include <type_traits>

template <class type_t>
class Container {
public:
    

    // Constructor 1
    template <typename... args_t>
    Container(args_t&&... args) :
        m_currentIndex{}
    {
        // pre alocate memory to avoid resizing
        m_values.reserve(sizeof...(args_t));

        // use a fold expression to put all values in the vector
        (m_values.emplace_back(std::forward<args_t>(args)),...);
    }

    // Constructor 2, more typesafe
    template<std::size_t N>
    Container(const type_t (&values)[N]) :
        m_values{std::begin(values),std::end(values)}
    {
    }

    // return a const& here so you avoid a copy
    const type_t& operator()() const 
    {
        return m_values[m_currentIndex];
    }

protected:
    size_t m_currentIndex;
    std::vector<type_t> m_values;
};

int main() 
{
    // Constructor 1
    Container<std::string> container1{"10", "20", "30"};    // ok
    Container<int> container2{10, 20, 30};                  // ok
    Container<size_t> container3{10, 20, 30};               // ok

    // Constructor 2
    Container<std::string> container4{{"10", "20", "30"}};    // ok
    Container<int> container5{{10, 20, 30}};                  // ok
    Container<size_t> container6{{10, 20, 30}};               // ok
}
3赞 user12002570 11/5/2023 #3

问题是这不是一个常量表达式(因为它是一个函数参数),所以从 to 的转换是一个缩小的转换。valuesvaluessize_t

这可以从 dcl.init.list#7 中看出,其中指出:

缩小转换是从整数类型或无作用域枚举类型到整数类型的隐式转换,该类型不能表示原始类型的所有值,除非源是常量表达式,其整数升级后的值将适合目标类型。

(强调我的)

基本上,这与为什么会起作用但不起作用的原因相同。同样,会起作用。std::vector<size_t> v{10, 20, 30}int i = 0; std::vector<size_t> v{i, 20, 30};const int i = 0; std::vector<size_t> v{i, 20, 30};

std::vector<size_t> v{10, 20, 30}; //ok because 10, 20 and 30 all are constant expressions

int i = 0; //i is not a constant expression
std::vector<size_t> v{i, 20, 30}; //not ok because i is not a constant  expression and so narrowing conversion

const int  j= 0; //j is a constant expression 
std::vector<size_t> v{j, 20, 30}; //ok because j is a constant expression

解决此问题的一种方法是为每个文字显式写入后缀。U

Container<size_t> container3(10U, 20U, 30U);
3赞 康桓瑋 11/5/2023 #4

您可以转发构造实际的 ,以便通过ValueTypesValueTypestd::vector<ValueType>std::initializer_list<ValueType>

template <typename... ValueTypes>
Container(ValueTypes&&... values) :
    currentIndex(0),
    values{ ValueType(std::forward<ValueTypes>(values))... }
{}

演示