【问题标题】:Assignment of initializer list初始化列表的赋值
【发布时间】:2014-04-20 19:39:16
【问题描述】:

下面的代码是我的问题的一个最小示例。我创建了一个包含固定大小数组的简单模板类,并重载了赋值运算符以接受定义方法size()begin() 的任何类(例如initializer_lists)。我不明白为什么 g++ 无法解析我对该运算符的调用(我使用的是 gcc 4.6):

***.cpp: In function ‘int main()’:
***.cpp:46:22: error: no match for ‘operator=’ in ‘a = {42, -1.0e+0, 3.14158999999999988261834005243144929409027099609375e+0}’
***.cpp:46:22: note: candidates are:
***.cpp:23:8: note: template<class U> A<T, N>::self& A::operator=(const U&) [with U = U, T = double, unsigned int N = 3u, A<T, N>::self = A<double, 3u>]
***.cpp:8:7: note: A<double, 3u>& A<double, 3u>::operator=(const A<double, 3u>&)
***.cpp:8:7: note:   no known conversion for argument 1 from ‘<brace-enclosed initialiser list>’ to ‘const A<double, 3u>&’
***.cpp:8:7: note: A<double, 3u>& A<double, 3u>::operator=(A<double, 3u>&&)
***.cpp:8:7: note:   no known conversion for argument 1 from ‘<brace-enclosed initialiser list>’ to ‘A<double, 3u>&&’

第一个候选者被正确列出,但没有相关的错误消息。代码如下:

#include <iostream>
#include <algorithm>
#include <initializer_list>

// ------------------------------------------------------------------------

template <typename T, unsigned N>
class A
{
public:

    typedef A<T,N> self;

    // Default ctor
    A() {}

    // Copy ctor
    template <typename U>
    A( const U& other ) { operator=(other); }

    // Assignemnt
    template <typename U>
    self& operator= ( const U& other )
    {
        if ( other.size() == N )
            std::copy_n( other.begin(), N, m_data );
            return *this;
    }

    // Display contents
    void print() const
    {
        for ( unsigned i = 0; i < N; ++i )
            std::cout << m_data[i] << " ";
        std::cout << std::endl;
    }

private:
    T m_data[N];
};

// ------------------------------------------------------------------------

int main()
{
    A<double,3> a;
    a = {42,-1.0,3.14159};
    a.print();
}

有谁知道为什么这可能是模棱两可的,或者我做错了什么?


注意:理想情况下,我什至想用一个 A&lt;double,3&gt; a = {42,-1.0,3.14159}; 替换 main 的前两行,但我不确定如何,我目前收到以下错误:

***: In function ‘int main()’:
***:45:34: error: could not convert ‘{42, -1.0e+0, 3.14158999999999988261834005243144929409027099609375e+0}’ from ‘<brace-enclosed initialiser list>’ to ‘A<double, 3u>’

【问题讨论】:

标签: c++ c++11 operator-overloading initializer-list overload-resolution


【解决方案1】:

auto 不同,将花括号初始化列表推导出为initializer_list,模板实参推导将其视为非推导上下文,除非存在initializer_list&lt;T&gt; 类型的相应参数,其中可以推导出T的情况。

来自 §14.8.2.1/1 [temp.deduct.call](已添加重点)

模板参数推导是通过将每个函数模板参数类型(称为P)与调用的相应参数类型(称为A)进行比较来完成的,如下所述。如果从 P 中删除引用和 cv 限定符会为某些 P0 提供 std::initializer_list&lt;P0&gt; 并且参数是初始化器 list (8.5.4),然后对初始化列表的每个元素执行推导,将P0 作为函数模板参数类型,并将初始化元素作为其参数。 否则,初始化列表参数会导致参数被视为非推导上下文 (14.8.2.5)。

因此,operator= 的参数不会被推断为initializer_list&lt;double&gt;。要使代码正常工作,您必须定义一个带有 initializer_list 参数的 operator=

template <typename U>
self& operator= ( const std::initializer_list<T>& other )
{
    if ( other.size() == N )
        std::copy_n( other.begin(), N, m_data );
    return *this;
}

【讨论】:

  • 谢谢,这是我问其他人的解释,我的错误现在很清楚了。再次非常感谢你:)
  • @Sh3ljohn 我在 juan 的回答下面看到了你的评论,你说这只是你编造的一个例子,但是(没有看到你的实际实现)这对我来说也不是一个好主意.如果您有一个封装固定大小数组的类,您的复制构造函数/赋值运算符可能应该只接受包含相同大小数组的另一个实例,而不是有条件地复制或抛出异常。
  • 如果输入容器的大小不同,我的“真实”代码会发出错误消息并且不会复制(无一例外)。你觉得这听起来不错?
【解决方案2】:

大括号括起来的初始化器列表不一定具有std::initializer_list&lt;T&gt; 类型,因此您需要指定赋值运算符模板需要std::initializer_list

  template <typename U>
  A& operator=(std::initializer_list<U> other )
  {
    if ( other.size() == N )
      std::copy_n( other.begin(), N, m_data );
    return *this;
  }

  A& operator=(std::initializer_list<double> other )
  {
    if ( other.size() == N )
      std::copy_n( other.begin(), N, m_data );
    return *this;
  }

我必须说,如果大小不匹配,赋值运算符会静默失败,这似乎不是一个好主意。

【讨论】:

  • 与@ooga 相同,我很想知道比我需要做的更多的事情才能让它工作,你介意解释一下发生了什么吗?对于你后面的担心,这是一个最小的例子,在这种情况下我的代码不会静默失败。
  • @Sh3ljohn 我在答案的开头添加了一些内容。我认为大括号括起来的初始化列表映射到std::initializer_list 的情况是有限的。你必须在这里告诉编译器你想要什么。
  • 谢谢 :) 我更喜欢@Praetorian 的回答,标准解释了为什么会发生这种情况,尽管我不确定他们为什么选择这样处理。
【解决方案3】:

我会说是这样的:

A<double,3> a;
a = {42,-1.0,3.14159};

您正在使用默认构造函数初始化 a,然后尝试在已初始化的对象上使用初始化列表 - 它抱怨缺少适当的 operator= 重载。而是尝试:

A<double,3> a = {42,-1.0,3.14159};

编辑:

你也没有定义必需的构造函数:

template <typename T, unsigned N>
class A
{
public:
    A(std::initializer_list list) : m_data(list) {}

//...
}

【讨论】:

  • a = {42,-1.0,3.14159}; 没有第二次初始化我的对象,我正在使用模板赋值运算符。对于您的建议,请阅读我的帖子末尾。
  • ... 请在下次编辑之前完整阅读我的帖子。定义了一个模板化的构造函数,我在第一部分没有使用这个构造函数。
  • 如果你隐藏了重要的细节,然后因为不知道而投反对票,没有人能够知道你的代码是如何工作的。
  • 不是一个完美的来源,但在这里 en.cppreference.com/w/cpp/utility/initializer_list 它可以工作 - 它只需要 T 将初始化列表作为有效的构造函数参数并将 T x[y] 替换为一些 std 集合 T。跨度>
猜你喜欢
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 2019-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-28
  • 2014-05-25
相关资源
最近更新 更多