Передать временный объект стандартным конструктором - PullRequest
3 голосов
/ 29 февраля 2012

Я хотел бы передать временный объект (например, std :: string) в конструктор моего объекта:

class MyClass{
  public:
    MyClass(string a):
      a(a)
    {

    }
    string a;
};

int main(int argc, char *argv[]){
  MyClass a(string());
  cout<<a.a<<endl;
  return 0;
}

Но я получаю эту ошибку:

main.cpp: In function ‘int main(int, char**)’:
main.cpp:28:11: error: request for member ‘a’ in ‘a’, which is of non-class type ‘MyClass(std::string (*)()) {aka MyClass(std::basic_string<char> (*)())}’

Все работает нормально, если я передаю что-либо конструктору временного объекта (например, string ("")).Почему?

Ответы [ 2 ]

8 голосов
/ 29 февраля 2012

Это пример того, что было названо C100 самым неприятным синтаксическим анализом .Компилятор интерпретирует

MyClass a(string());

как прототип функции с именем a, возвращающей MyClass и принимающей неназванный string в качестве параметра.

Вы можете устранить неоднозначность, поставивСкобки вокруг вызова к конструктору string:

MyClass a((string()));

Или, если MyClass имеет доступный конструктор копирования:

MyClass a = string();

Обновление для C ++ 11

Вы также можете устранить неоднозначность с помощью универсального синтаксиса инициализации, если у вашего класса нет конструктора, который принимает initializer_list:

MyClass a { string() };
2 голосов
/ 29 февраля 2012

Как уже указывал Сет, это проблема с языком и тем, как анализируется выражение. В основном, когда компилятор находит выражение MyClass a(string()), он интерпретирует его как объявление функции a, которая имеет сигнатуру MyClass (std::string (*)()) (дополнительная (*) получается из неявного преобразования функции в указатель на функцию в аргументах).

Существуют различные подходы к преодолению этого синтаксиса в вашем конкретном случае:

MyClass a("");                // 1
MyClass b = std::string();    // 2
MyClass c(( std::string() )); // 3

Первый подход заключается не в использовании выражения T() для создания значения r, а в константном литерале, который будет выдавать тот же результат. Недостатком этого подхода является то, что в данном конкретном случае вы можете использовать литерал, но это же решение не может быть применено к другим типам (т. Е. Если у вас был свой собственный тип, у которого был только конструктор по умолчанию)

Второй подход позволяет избежать прямой инициализации, поскольку альтернативный синтаксис не может быть проанализирован как объявление функции. Проблема с этим подходом состоит в том, что, хотя результат в вашем конкретном случае одинаков, он требует, чтобы конструктор не был явным . (То есть, если ваш конструктор был объявлен explicit MyClass( std::string const & ), он потерпит неудачу, но это не будет

Третий подход заключается в добавлении дополнительного набора скобок вокруг первого аргумента в конструктор (обратите внимание, что та же проблема при синтаксическом анализе произошла бы с MyClass a(std::string(), std::string())). Проблема с этим подходом (мнением) в том, что он уродлив, но в остальном он самый гибкий из всех.

...