Из моего чтения ответов на SO и Ссылка на ссылку
Унаследованные конструкторы эквивалентны пользовательским конструкторам с пустым телом и списком инициализатора члена, состоящим из одного вложенное имя спецификатор, который передает все свои аргументы в конструктор базового класса.
Я пришел к выводу, что ниже классы D
а также E
должен вести себя одинаково
#include <string>
#include <utility>
using namespace std;
class B
{
public:
B(string&& a) : a(move(a))
{
}
string a;
};
class D : public B
{
public:
using B::B;
};
class E : public B
{
public:
E(string&& a) : B(a)
{
}
};
string foo()
{
return "bar";
}
int main()
{
D d = foo();//This compiles
E e = foo();//This does not compile
return 0;
}
E e = foo()
по ошибке не компилируется, так как B
конструктор принимает только string&&
, Тем не мение, D d = foo()
проходит нормально. Это почему?
Используется компилятор clang3.5.
РЕДАКТИРОВАТЬ: Кроме того, как объяснено в этом ответ, идеальный способ пересылки не заменяет наследование конструкторов. Итак, как именно выглядит тело?
Однако D d = foo () проходит нормально. Это почему?
Так как using B::B
эффективно передает временную строку прямо в B
конструктор, где его еще можно связать аля &&
(то есть его категория стоимости все еще xvalue), затем выполняет любую дополнительную инициализацию производного класса (если были другие члены данных, VDT и т. д.). Это очень желательно, так как смысл использования конструкторов базового класса состоит в том, чтобы разрешить одинаковое использование клиента.
(Это контрастирует с E(string&&)
внутри которого, где по имени a
параметр больше не считается значением xvalue (временно истекающим), готовым для передачи B::B
.)
(Если вы этого еще не сделали, вы можете посмотреть наstd::forward
) [http://en.cppreference.com/w/cpp/utility/forward] тоже … это помогает с идеальной передачей аргументов)
Формулировка стандарта о том, как выглядит определение унаследованного конструктора в производном классе, немного более явна, чем то, на что ссылается описание cppreference, но ключевая фраза в последнем выдвигает все свои аргументы. Другими словами, категория значений аргументов сохраняется, что ваше определение E
не делает, и, следовательно, не компилируется.
От N3337, §12.9 / 8 [Class.inhctor]
...
Неявно определенный наследующий конструктор выполняет набор инициализаций класса, который будет выполнен встроенным конструктором, написанным пользователем для этого класса с мем-инициализатора-лист только чей мем-инициализатор имеет мем-инициализатор-идентификатор который называет базовый класс, обозначенный в вложенное имя спецификатор из используя декларирование и список_выражений как указано ниже, и где составной оператор в его теле функции пуст (12.6.2). Если этот пользовательский конструктор будет плохо сформирован, программа будет некорректно сформирована. Каждое выражение в список_выражений имеет формуstatic_cast<T&&>(p)
, гдеp
это имя соответствующего параметра конструктора иT
это объявленный типp
,
Таким образом, аргументы конструктора отлично перенаправляются в соответствующий унаследованный конструктор (std::forward
(§20.2.3) указано вернуть static_cast<T&&>(p)
точно так же, как и описание выше). В зависимости от объявленного типа параметра конструктора свертывание ссылок происходит, как описано в этот ответ.
В твоем случае, [T=string&&]
и бросок дает string&&
опять же, который может связываться с B
параметр. Чтобы соответствовать этому поведению в E
вы должны переписать конструктор как
E(string&& a) : B(static_cast<string&&>(a))
{
}