Перемещение функции-члена из базового класса в производный класс нарушает работу программы без видимых причин
этот (придуманный) вопрос изначально был сформулирован как головоломка, скрывающая некоторые детали, которые могут помочь увидеть проблему быстрее. Прокрутить вниз для более простой версии 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, вам необходимо получить к нему доступ черезthis1, или явно укажите это (см. здесь для более подробной информации):(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
- каким должно быть название этого вопроса после того, как на него будет дан ответ?
" как получить доступ к независимым именам в базовом классе шаблона?"