Перемещение функции-члена из базового класса в производный класс нарушает работу программы без видимых причин
этот (придуманный) вопрос изначально был сформулирован как головоломка, скрывающая некоторые детали, которые могут помочь увидеть проблему быстрее. Прокрутить вниз для более простой версии MCVE.
оригинал (а-ля головоломка) версия
у меня есть этот кусок кода, который выводит 0
:
#include <iostream>
#include <regex>
using namespace std;
regex sig_regex("[0-9]+");
bool oldmode = false;
template<class T>
struct B
{
T bitset;
explicit B(T flags) : bitset(flags) {}
bool foo(T n, string s)
{
return bitset < 32 // The mouth is not full of teeth
&& 63 > (~n & 255) == oldmode // Fooness holds
&& regex_match(s, sig_regex); // Signature matches
}
};
template<class T>
struct D : B<T>
{
D(T flags) : B<T>(flags) {}
};
int main()
{
D<uint64_t> d(128 | 16 | 1);
cout << d.foo(7, "123") << endl;
}
однако, когда я переместить функцию foo()
С B
до D
начинается вывод 1
(доказательство находится на Coliru).
почему это происходит?
версия MCVE
#include <iostream>
#include <bitset>
using namespace std;
template<class T>
struct B
{
T bitset{0};
bool foo(int x)
{
return bitset < 32 && 63 > (x + 1) == x % 2;
}
};
template<class T>
struct D : B<T>
{
bool bar(int x) // This is identical to B<T>::foo()
{
return bitset < 32 && 63 > (x + 1) == x % 2;
}
};
int main()
{
D<uint64_t> d;
cout << d.foo(1) << endl; // outputs 1
cout << d.bar(1) << endl; // outputs 0; So how is bar() different from foo()?
}
4 ответа:
вот почему вы никогда не должны
using namespace std;
bool foo(T n, string s) { return bitset < 32 && 63 > (~n & 255) == oldmode && regex_match(s, sig_regex); }
это
bitset
это не то, что вы думаете. Потому чтоB<T>
является зависимым базовым классом, члены скрыты от неквалифицированного поиска. Так что доступbitset
, вам необходимо получить к нему доступ черезthis
1, или явно укажите это (см. здесь для более подробной информации):(this->bitset) B<T>::bitset
, потому что
bitset
не имяB<T>::bitset
в производном случае, что это может означать? Ну, потому что ты написалusing namespace std;
, это на самом делеstd::bitset
, и остальная часть вашего выражения просто так происходит, чтобы быть действительным. Вот что происходит:bool foo(T n, string s) { return std::bitset<32 && 63>(~n & 255) == oldmode && regex_match(s, sig_regex); }
The
32 && 63
оценивает вtrue
, который произведен в1u
наstd::bitset
аргумент шаблона. Этоstd::bitset
инициализируется с~n & 255
, и проверяется на равенство сoldmode
. Этот последний шаг действителен, потому чтоstd::bitset
имеет неявный конструктор, который позволяет временноstd::bitset<1>
быть изготовлены изoldmode
.
1 обратите внимание, что мы должны заключить в скобки
this->bitset
в этом случае из-за довольно тонкого анализа disambiguity правила. Смотрите шаблон зависимый элемент базы не решается должным образом для сведения.
Да, потому что
bitset
будет интерпретироваться как несамостоятельное имя, и есть шаблон с именемstd::bitset<T>
, следовательно, он будет считан как:template<class T> struct D : B<T> { D(T flags) : B<T>(flags) {} bool foo(T n, string s) { return ((std::bitset < 32 && 63 > (~n & 255)) == oldmode) && regex_match(s, sig_regex); } };
вы должны сделать это так:
template<class T> struct D : B<T> { D(T flags) : B<T>(flags) {} bool foo(T n, string s) { // or return B<T>::bitset return (this->B<T>::bitset < 32) // The mouth is not full of teeth && 63 > (~n & 255) == oldmode // Fooness holds && regex_match(s, sig_regex); // Signature matches } };
или лучше, не использовать
using namespace std;
- почему это происходит?
для производного класса,
B<T>
не является независимым базовым классом, он не может быть определен без знания аргумента шаблона. Иbitset
- это несамостоятельное имя, которое не будет искать в зависимом базовом классе. Вместо этого,std::bitset
используется здесь (из-заusing namespace std;
). Так что вы получите:return std::bitset<32 && 63>(~n & 255) == oldmode && regex_match(s, sig_regex);
вы могли бы сделать имя
bitset
зависимый; потому что зависимые имена могут быть найдены только во время создания экземпляра и в это время будет известна точная базовая специализация, которая должна быть изучена. Например:return this->bitset < 32 // The mouth is not full of teeth // ~~~~~~ && 63 > (~n & 255) == oldmode // Fooness holds && regex_match(s, sig_regex); // Signature matches
или
return B<T>::bitset < 32 // The mouth is not full of teeth // ~~~~~~ && 63 > (~n & 255) == oldmode // Fooness holds && regex_match(s, sig_regex); // Signature matches
или
using B<T>::bitset; return bitset < 32 // The mouth is not full of teeth && 63 > (~n & 255) == oldmode // Fooness holds && regex_match(s, sig_regex); // Signature matches
- каким должно быть название этого вопроса после того, как на него будет дан ответ?
" как получить доступ к независимым именам в базовом классе шаблона?"