2013-06-20 3 views
8

Можно ли написать такой класс, что они действительны:Могу ли я перегрузить неявную инициализацию на 0?

Foo a; 
Foo b = 0; 
Foo c = b; 
Foo d(0); 
Foo e(1); 
Foo f = Foo(1); 

Но это не так:

int x; 
Foo a = x; 
Foo b = 1; 
Foo c = 2; 
//etc 

По сути, мое правило «Константа 0 неявно конвертируемым в Foo , но никакое другое значение не является «

+0

Как насчет 'Foo e (1);'? Как это справедливо? –

+0

@LuchianGrigore: Являются ли они семантически идентичными? Если это так, я думаю, я могу объявить это недействительным. – Eric

+1

Может быть, вы можете попробовать с конструктором, принимающим 'std :: nullptr_t' (просто идея ...) –

ответ

5

Если вы не возражаете против Foo b = nullptr; работающих, это довольно легко взломать. Имейте явный конструктор от int и неявный от std::nullptr_t.

Если вы не против работать, я не уверен, что это возможно. Единственный способ отличить литерал 0 и другие целочисленные литералы - это неявное преобразование первого в указатели и nullptr_t. Таким образом, nullptr предпочтет параметр nullptr_t параметру указателя, поэтому, имея оба конструктора, вы можете отфильтровать аргументы nullptr. Однако переходы 0 на указатели и nullptr_t имеют одинаковый ранг, поэтому это может привести к уклонению аргументов 0 с двусмысленностью.

Хм ... что-то подобное может работать:

class Foo { 
    struct dummy; 
public: 
    explicit Foo(int); // the version that allows Foo x(1); 
    Foo(dummy*); // the version that allows Foo x = 0; 
    template <typename T, 
      typename = typename std::enable_if< 
       std::is_same<T, std::nullptr_t>::value>::type> 
    Foo(T) = delete; // the version that prevents Foo x = nullptr; 
}; 

Я на самом деле не пробовал. Теоретически шаблон должен участвовать только в разрешении перегрузки, когда аргумент равен nullptr, поскольку в противном случае SFINAE убивает его. В этом случае, однако, он должен быть лучше конструктора указателя.

0
Foo e(1); 

Будет ли вызывать неявный конструктор Foo, принимающий int как аргумент. По существу эта строка будет делать то же самое, пытаясь преобразовать int в Foo, используя этот конструктор int.

Foo b = 1; 

Запрещается обрабатывать определенные значения этого int напрямую. Если у вас есть свой конструктор explicit, вы не сможете написать и следующую строку.

Foo b = 0; 

gx_ верно заявил, что 0 может быть преобразован в std :: nullptr_t. Следующие действия будут действовать в отношении ваших намерений.

Foo(std::nullptr_t x) : member(0) { } 
explicit Foo(int c) : member(c) { } 
// ... 
Foo a = 0; // compiles 
Foo b = 1; // doesn't compile 

// Note: 
int do_stuff (void) { return 0; } 
Foo c = do_stuff(); // doesn't compile 
+0

Нет, прямое копирование-инициализация. –

+1

Как насчет 'явного Foo (int);' плюс 'Foo (nullptr_t);'? –

+0

+1, скорее боковая мысль, но возможно – Pixelchemist

-1

Одна идея у меня было:

Foo(const uint32_t c) : member(0) { static_assert(c == 0, "Nope"); } 
explicit Foo(uint32_t c) : member(c) { } 

ли себя это разумно?

+2

Это даже не компилируется. Что касается разрешения перегрузки, параметр 'const uint32_t' и параметр' uint32_t' - это одно и то же. –

-1

Я признаю, что я не достиг полного овладения RValue семантики C++ 11 еще, но это, кажется, что вы хотите:

class Foo 
{ 
    public: 
    Foo(int&&) {} 
}; 

int main() 
{ 
    Foo a(123); 
    int x = 123; 
    Foo b(x); // error here, line 11 
    return 0; 
} 

Результат:

prog.cpp:11: error: cannot bind ‘int’ lvalue to ‘int&&’

Комментарии приветствуем, если в этом коде есть какие-либо оговорки, которые я не заметил, или если вы можете успокоить меня, что это не так.

+0

Интересно, но я нацелен на конкретный случай 'x == 0'; – Eric

+0

@eric Ах, не понимал этого. Вы сказали, что 'Foo e (1);' должны быть действительными: / – Oktalist

Смежные вопросы