Я пытаюсь найти лучший способ предотвратить неявное приведение целого числа 0 к nullptr_t, а затем передать конструкторам, которые принимают указатели. Явное не делает этого, но я могу получить nullptr_t, чтобы вызвать неоднозначную ошибку перегрузки:
#include <typeinfo.h>
struct A {
explicit A(char*) { }
};
struct B {
B(nullptr_t a) = delete;
B(char*) { }
};
int main(int argc, char* argv[])
{
A a(0); // darnit I compiled...
B b1(0); // good, fails, but with only b/c ambiguous
B b2((char*)0); // good, succeeds
B b3(1); // good, fails, with correct error
}
Есть ли лучший способ, чем этот? Кроме того, что именно здесь выполняет удаление?
Удаление A(int)
не помешает A(char *)
вызывается с
nullptr
, Так что вам нужно будет удалить A(nullptr_t)
также.
И даже это не защитит вас от того, что
в районе есть какой-то изгоев
неявно конструируемый от 0 или nullptr
а также
неявно преобразует то же самое в char *
,
#include <iostream>
struct A {
A(int) = delete;
A(std::nullptr_t) = delete;
explicit A(char * p) {
std::cout << "Constructed with p = " << (void *)p << std::endl;
}
};
struct X
{
X(long i) : _i(i) {}
operator char *() const { return (char *)_i; }
long _i;
};
int main()
{
X x(0);
A a(x);
return 0;
}
Программа печатает:
Constructed with p = 0
Вы можете считать эту возможность достаточно отдаленной, чтобы ее можно было игнорировать;
или вы можете удалить все конструкторы,
одним махом, чей аргумент не совсем типа
что ты санкционируешь. Например. при условии, что санкционированные типы просто char *
:
struct A {
template<typename T>
A(T) = delete;
explicit A(char * p) {}
};
struct X
{
X(long i) : _i(i) {}
operator char *() const { return (char *)_i; }
long _i;
};
int main()
{
// A a0(0);
//A a1(nullptr);
X x(0);
// A a2(x);
char *p = x;
A a3(p); // OK
return 0;
}
Здесь все закомментированные вызовы конструктора не компилируются.
Если вы хотите остановить ваш конструктор от принятия 0
один вариант будет удалить B(int)
:
B(int) = delete;
Это однозначно лучше подходит для B(0)
чем конструктор, который принимает char *
,
Обратите внимание, что до C ++ 11, NULL
был задан как целочисленный тип, и даже в C ++ 11 и более поздних версиях он все еще, вероятно, реализован как #define NULL 0
, B(NULL)
не сработает, если вы это сделаете. B(nullptr)
будет работать, но все же, я бы опасался, стоит ли это делать.