C ++ объявляет функцию вместо вызова сложного конструктора - PullRequest
6 голосов
/ 19 марта 2012

Во-первых, я знаю, что уже есть похожие вопросы по stackoverflow ( это , это и это ), и именно поэтому я понимаю, почемумоя проблема.К сожалению, это не помогает мне решить эту проблему.

Хотя все вышеупомянутые вопросы касаются конструктора по умолчанию, без параметров, у меня возникла проблема при использовании конструктора с двумя параметрами со значениями по умолчанию - Я пытался создать объект, вызывающий конструктор, используя только первое заданное значение, и он анализируется как объявление функции вместо объекта.

Вот некоторые фрагменты моего кода (я переименовал классимена, потому что они длинные и неактуальные):

class algoContainer{
public:
algoContainer(algo1Virtual &alg1 = algo1Concrete::emptyInstance(),
      algo2Virtual &alg2 = algo2Concrete::instance());

someUsefulFunction();
};

class algo1Concrete : public algo1Virtual{
    private:
    algo1Concrete();
    public:
    static algo1Concrete &emptyInstance(); // "empty" instance treated
                                           // specifically
                                           //  -- uses private no arg constructor
    algo1Concrete(const std::vector<data> &myData); // construcotr
};

class algo1Virtual{
    // ... all functions virtual, no implementations ...
};


// ... similar for algo2Virtual/Concrete ...

Все функции в классах Concrete реализованы, в то время как ни одна из них в классах Virtual не является (кроме конструкторов и деструкторов).).

Итак, моя проблема в том, что Я хочу сделать что-то вроде :

std::vector <data> workData;
// fill workData
algoContainer myAC(algo1Concrete(workData));
myAC.someUsefulFunction(); // this line gives compile error

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

Я проверил свой код, если я использую конструктор свсе параметры по умолчанию, и даже если я просто создаю первый параметр отдельно:

std::vector <data> workData;
// fill workData
algo1Concrete myA1(workData);
algoContainer myAC(myA1);

myAC.someUsefulFunction(); // now it works fine

algoContainer myAC2;
myAC2.someUsefulFunction(); // this also works

Я могу использовать код как есть, но будет очень признателен, если кто-нибудь может дать мне более элегантное решениена тот, который я использую сейчас.


РЕДАКТИРОВАТЬ : сообщения об ошибках, которые я получаю, когда исправляю самый неприятный анализ

Если я использую код с круглыми скобками:

algoContainer myAC((algo1Concrete(workData)));

Мои ошибки:

/some_path/main.cpp:47:65: error: no matching function for call to ‘algoContainer::algoContainer(algo1Concrete)’
/some_path/main.cpp:47:65: note: candidates are:
/some_path/algo/algocont.h:45:5: note: algoContainer::algoContainer(algo1Virtual&, algo2Virtual&)
/some_path/algo/algocont.h:45:5: note:   no known conversion for argument 1 from ‘algo1Concrete’ to ‘algo1Virtual&’
/some_path/algo/algocont.h:36:7: note: algoContainer::algoContainer(const algoContainer&)
/some_path/algo/algocont.h:36:7: note:   no known conversion for argument 1 from ‘algo1Concrete’ to ‘const algoContainer&’

Я переименовал пути и вставил файл примера и имена классов (такие же, как указано выше) для удобства чтения,Просто замечание: line 45 - это определение рассматриваемого конструктора.С другой стороны, line 36 - это строка class algoContainer.

Я тоже пытался с этим кодом:

algoContainer myDect((algo1Virtual)(algo1Concrete(workData)));

И тогда ошибки совершенно другие:

/some_path/main.cpp:47:86: error: cannot allocate an object of abstract type ‘algo1Virtual’
/some_path/algo/alg1/algo1virtual.h:31:7: note:   because the following virtual functions are pure within ‘algo1Virtual’:
/some_path/algo/alg1/algo1virtual.h:42:8: note:     virtual algo1Virtual::~algo1Virtual()
/some_path/algo/alg1/algo1virtual.h:39:18: note:    virtual void algo1Virtual::someAlgo1Function(std::vector<data>&)
/some_path/main.cpp:47:87: error: no matching function for call to ‘algoContainer::algoContainer(algo1Virtual)’
/some_path/main.cpp:47:87: note: candidates are:
/some_path/algo/algocont.h:45:5: note: algoContainer::algoContiner(algo1Virtual&, algo2Virtual&)
/some_path/algo/algocont.h:45:5: note:   no known conversion for argument 1 from ‘algo1Virtual’ to ‘algo1Virtual&’
/some_path/algo/algocont.h:36:7: note: algo1Virtual::algo1Virtual(const algo1Virtual&)
/some_path/algo/algocont.h:36:7: note:   no known conversion for argument 1 from ‘algo1Virtual’ to ‘const algo1Virtual&’

Надеюсь, это поможет.

Ответы [ 4 ]

3 голосов
/ 20 марта 2012

Кажется, проблема связана с аргументами, принятыми конструктором:

algoContainer( algo1Virtual &alg1,
               algo2Virtual &alg2 );

Примечание. Я удалил аргументы по умолчанию для краткости.

это принимает аргументы как неконстантные ссылки. Итак, когда вы делаете звонок, как:

algoContainer myAC(algo1Concrete(workData));

конструкция:

algo1Concrete(workData)

приводит к построению анонимного временного. Анонимные временные пользователи не могут связываться с неконстантными ссылками, просто потому что они являются временными и любые изменения, которые вы можете внести в них, мгновенно исчезнут (это не реальная причина, но, кажется, имеет смысл. Это ничего не значит для изменения анонимного временный, так как у вас нет возможности использовать его позже (без имени) или в конечном итоге (его временно)). На самом деле неконстантные ссылки могут связываться только с l-значениями, а анонимные временные значения являются r-значениями. (Подробности: Неконстантная ссылка может быть привязана только к lvalue )

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

Передача по значению будет выглядеть так:

algoContainer( algo1Virtual alg1,
               algo2Virtual alg2 );

Это приведет к ненужным копиям.

Другой вариант - передать ссылку на rvalue в C ++ 11, например:

algoContainer( algo1Virtual &&alg1,
               algo2Virtual &&alg2 );

Теперь ваше первое использование будет работать из коробки:

std::vector <data> workData;
// fill workData
algoContainer myAC(algo1Concrete(workData));
myAC.someUsefulFunction();

, но при повторном использовании вам нужно будет изменить его так, чтобы ваш объект «перемещался» в конструктор, и algoContainer становится владельцем данных (тогда локальная переменная names является «плохой» и НЕ должна использоваться вообще).

std::vector <data> workData;
// fill workData
algo1Concrete myA1(workData);
algoContainer myAC(std::move(myA1)); //NOTICE THE std::move call.
//myA1 is now a dummy, and unusable as all the internals have gone.
myAC.someUsefulFunction(); 

Чтобы этот пример работал, вам нужно реализовать конструктор перемещения для algo1Concrete со следующей подписью:

algo1Concrete ( algo1Concrete&& other )

, который просто переведет внутренние данные в текущий и оставит "другое" в неопределенном состоянии. (Подробности: http://msdn.microsoft.com/en-us/library/dd293665.aspx)

ПРИМЕЧАНИЕ. Относительно аргументов по умолчанию.

Я обычно рекомендую избегать использования аргументов по умолчанию для функций, поскольку они приводят к большей путанице, чем к удобству. Все параметры по умолчанию можно «смоделировать», просто перегружая функцию. Таким образом, в вашем случае у вас будет три ctors:

algoContainer(); //This assumes that the args were both the statics
algoContainer( algo1Virtual alg1 ); //This assumes that arg2 was the static.
algoContainer( algo1Virtual alg1, algo2Virtual alg2 ); //This uses both input.

Я согласен, что он более многословный, и не так много компиляторов в настоящее время реализуют наследующие конструкторы, поэтому мы также довольно часто копировали код. Но это изолирует одну из ряда проблем отладки / магической ценности, которые возникают при исследовании проблемы. Но, FWIW, это просто мнение.

3 голосов
/ 19 марта 2012

algoContainer myAC (algo1Concrete (workData));

Эта строка недопустима. Вы не можете связать rvalue с изменяемой ссылкой lvalue. Это должно быть const - и даже если бы это было так, объект умрет, прежде чем его сможет использовать любая функция, кроме конструктора.

0 голосов
/ 19 марта 2012

В качестве альтернативы текущему решению вы можете использовать дополнительную пару скобок или инициализацию копирования.

0 голосов
/ 19 марта 2012

Напишите это:

algoContainer myAC((algo1Concrete(workData)));

Тогда поищите в интернете "самый неприятный анализ". Есть также сотни дубликатов этого вопроса здесь, на StackOverflow, но их трудно найти, потому что проблема никогда не идентифицируется в самом вопросе. (Если бы это было, не было бы вопроса.)


(После вашего редактирования :) Временные объекты (например, созданные вызовом функции, которая возвращается по значению) не привязываются к непостоянным ссылкам. Вы должны сказать:

algo1Concrete ac = algo1Concrete(workData);
algoContainer myAC(ac);
...