Неоднозначность конструктора с инициализатором массива - PullRequest
4 голосов
/ 04 февраля 2020

У меня проблема в том, что я не могу использовать определенный конструктор с инициализатором массива при использовании VS2017 (C ++ 14, C ++ 17 и последняя версия ISO).

Я получаю ошибку C2397 conversion from 'double' to 'unsigned int' requires a narrowing conversion, когда он должен вызывать конструктор с контейнером, заполненным одним элементом.

#include <vector>

class obj
{
public:
    obj(const std::vector<double>& values, unsigned int stride)
        : values_(values), stride_(stride)
    {
    }

    obj(unsigned int m, unsigned int n)
        : stride_(n)
    {
    }

private:
    unsigned int stride_;
    std::vector<double> values_;
};

int main(int argc, char** argv)
{
    obj m(1, 1);                // correct constructor called.
    obj mm({ 42.0 }, 1);        // Error C2397

    return 0;
}

Я могу исправить это явное объявление контейнера ...

    obj mm(std::vector<double>({ 42.0 }), 1);

Или инициализация контейнера более чем одним элементом ...

    obj mm({ 42.0, 12.0 }, 1);

Последний, очевидно, бесполезен, а первый немного раздражает, так как это угловой корпус для контейнеров с одним предметом (хотя и не конец света). Я подумал, что это может быть проблематично c только для двойников (без буквального объявления), однако это происходит даже для чисел с плавающей запятой при их инициализации с литералами тоже. то есть контейнер std::vector<float>, следующая строка по-прежнему содержит ошибку C2397 .

    obj mm({ 42.0f }, 1);

Я не склонен верить, что ошибки компилятора сами по себе не встречаются (хотя они, очевидно, существует), однако я не могу не думать, что это может быть один, или если нет, есть ли в стандарте упоминание, как справиться с этой ситуацией. В идеале я хотел бы иметь возможность использовать инициализатор массива без явного объявления типа контейнера, как я могу, когда в контейнере существует более одного элемента. Это возможно?

Ответы [ 3 ]

5 голосов
/ 04 февраля 2020

Использование {{ и }} является исправлением во всех случаях

obj mm({{ 42.0 }}, 1); 

и

obj mm({{ 42.0, 12.0 }}, 1);

Хотя, конечно, во втором случае не существует двусмысленности (использование одиночные скобки используют brace-elision ).

Этот вопрос дает хорошее введение в topi c: скобка elision в инициализации std :: array

1 голос
/ 04 февраля 2020

К объекту можно добавить конструктор с std::initializer_list.

Образец:

#include <iostream>
#include <vector>

struct Obj {
  std::vector<double> values;
  unsigned stride;

  Obj(std::initializer_list<double> values, unsigned stride = 1):
    values(values), stride(stride)
  {
    std::cout << "Obj::Obj(std::initializer_list<double>, unsigned)\n";
   }

  Obj(const std::vector<double> &values, unsigned stride = 1):
    values(values), stride(stride)
  {
    std::cout << "Obj::Obj(const std::vector<double>&, unsigned)\n";
  }

  Obj(unsigned m, unsigned stride = 1):
    stride(stride)
  {
    std::cout << "Obj::Obj(unsigned, unsigned)\n";
  }
};

int main()
{
  Obj mm({ 42.0f }, 1);
  Obj mm1(1, 1);
  Obj mm2(std::vector<double>({ 42.0 }), 1);
  Obj mm3({ 42.0, 12.0 }, 1);
  Obj mm4(std::vector<double>{ 42.0 }, 1);
}

Вывод:

Obj::Obj(std::initializer_list<double>, unsigned)
Obj::Obj(unsigned, unsigned)
Obj::Obj(const std::vector<double>&, unsigned)
Obj::Obj(std::initializer_list<double>, unsigned)
Obj::Obj(const std::vector<double>&, unsigned)

Демонстрация в реальном времени на coliru

1 голос
/ 04 февраля 2020

Вы имеете в виду следующее

obj mm({ 1, 42.0 }, 1);

или следующее

obj mm({ { 42.0 } }, 1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...