Вызов конструктора базового класса с объявленным псевдонимом в другом пространстве имен

Я пытаюсь понять некоторые детали классов, объявленных псевдонимами, через using С++ 11 и как/почему это влияет на вызов конструктора базового класса.

Пример кода

#include <iostream>

namespace N {

  template<typename T>
  struct Foo
  {
//     Foo(){}; // NOTE: If this line is present, error 2 goes away.
    Foo(T t):value(t){};
    ~Foo() = default;
    T value; 
  };

  struct BarInt : public Foo<int>
  {
    BarInt(int t):Foo< int >(t){};
    ~BarInt() = default;
  };

  using BarFloat = Foo<float>;

};

struct A : public N::BarInt
{
  A(int i=42):BarInt(i){}; //BarInt(i) or N::BarInt(i) doesn't matter here
  ~A() = default;
};

struct B : public N::BarFloat
{
  B(float f=23.0):BarFloat(f){}; //two errors with gcc4.7.2 (with -std=gnu++11)

//   B(float f=23.1):N::BarFloat(f){}; //this line seems to work.
  ~B() = default;
};


int main(int argc, char **argv)
{
  A a;
  B b;  
  std::cout << "a's value is "<< a.value << "\n"
            << "b's value is "<< b.value << std::endl;
  return 0;
}

gcc 4.7.2 (компиляция с -std=gnu++11) генерирует две ошибки для этого кода, которые, как мне кажется, связаны (хотя я не понимаю, как...)

Ошибка 1

main.cpp: In constructor ‘B::B(float)’:
main.cpp:32:19: error: class ‘B’ does not have any field named ‘BarFloat’

Мои поиски в stackoverflow привели к Требуется ли пространство имен при ссылке на базовый класс, который упоминает имя внедрённого класса в качестве отправной точки для дальнейшего поиска. Однако из того, что я собрал, это объясняет, почему я могу написать конструктор для A так, как я это сделал (то есть как A(int i=42):BarInt(i){};), и почему BarInt(i) не нужно квалифицировать с помощью пространства имен N.

Так почему же это не работает с B? Согласно В чем разница между "typedef" и " using' в C++11?, using совпадает со старым добрым typedef, поэтому я думаю, что мой вопрос для первой ошибки заключается в том, как класс, объявленный псевдонимом (BarFloat в моем примере), отличается от обычных классов (BarInt в моем примере) в контексте внедрения имен классов. Любые указатели приветствуются :)

Ошибка 2

main.cpp:32:29: error: no matching function for call to ‘N::Foo<double>::Foo()’
main.cpp:32:29: note: candidates are:
main.cpp:9:5: note: N::Foo<T>::Foo(T) [with T = double]
main.cpp:9:5: note:   candidate expects 1 argument, 0 provided
main.cpp:6:10: note: constexpr N::Foo<double>::Foo(const N::Foo<double>&)
main.cpp:6:10: note:   candidate expects 1 argument, 0 provided

Эта ошибка исчезает, если я, как уже отмечалось в примере кода выше, ввожу пустой конструктор Foo(). Однако у меня есть вопрос, почему BarFloat(f) вызывает вызов пустого конструктора Foo() и, в этом случае, как BarFloat.value может быть установлено на 23.0.

Пост скриптум

Поскольку это мой первый пост здесь: Привет, stackoverflow, и спасибо всем за огромную помощь, которую вы уже оказали мне, помогая другим с их проблемами!


person hcc23    schedule 16.01.2013    source источник


Ответы (1)


Когда вы наследуете от класса, производный класс может обращаться к именам базового класса (включая само имя базового класса) без уточнения. Вы фактически наследуете имена в базовом классе. Вот почему наследование от N::BarInt позволяет ссылаться на BarInt внутри A без уточнения.

Для B вы наследуете от Foo<double>, используя псевдоним BarFloat, но Foo<double> не содержит BarFloat, поэтому вы не наследуете это имя.

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

person Vaughn Cato    schedule 16.01.2013
comment
Спасибо за быстрый ответ, хотя у меня есть несколько дополнительных вопросов: для ошибки 1 вы эффективно объясняете, почему это работает для BarInt, но у вас есть идеи, почему это не работает для BarFloat? А для ошибки 2 отсутствующий пустой конструктор: если компилятор не находит базу (BarFloat) из-за отсутствующего пространства имен, то откуда он знает о базе базы, т.е. Foo? - person hcc23; 16.01.2013
comment
@hcc23: Второй абзац описывает, почему это не работает для BarFloat. Возможно, я добавил это после того, как вы впервые увидели мой ответ. - person Vaughn Cato; 16.01.2013
comment
Ах, спасибо за второй абзац (который я, должно быть, пропустил, когда печатал свой первый комментарий), мне кажется, он имеет смысл. - person hcc23; 16.01.2013
comment
@ hcc23: компилятор обрабатывает текст в основном сверху вниз. Когда вы объявляете B, вы указываете, что оно имеет базу N::BarFloat, и это работает нормально, поскольку оно квалифицировано. В этот момент остальная часть класса теперь может видеть содержимое Foo<double>. Компилятор просто не видит BarFloat, потому что он не находится внутри Foo<double>. - person Vaughn Cato; 16.01.2013