2016-09-10 4 views
6

Я пишу класс, где у меня есть шаблонный конструктор и конструктор копирования. Каждый раз, когда я хочу вызвать конструктор копирования с объектом non const, выбирается шаблонный конструктор. Как заставить компилятор выбрать конструктор копирования?Форсировать компилятор, чтобы выбрать конструктор копирования с константой T & как параметр

Вот mcve:

#include <iostream> 

struct foo 
{ 
    foo() 
    { 
     std::cout << "def constructor is invoked\n"; 
    } 

    foo(const foo& other) 
    { 
     std::cout << "copy constructor is invoked\n"; 
    } 

    template <typename T> 
    foo(T&& value) 
    { 
     std::cout << "templated constructor is invoked\n"; 
    } 
}; 

int main() 
{ 
    foo first; 
    foo second(first); 
} 

Удаление функции не то, что я хочу.

+0

не должен заливка аргумента в 'сопзИ & Foo' при * вызова * в CTOR сделать он работу? Ctor для const args, так что давайте его. –

+0

@ PeterA.Schneider, я пишу 'std :: variant'. Я не думаю, что людям понравится кастинг. Я хочу, чтобы пользовательская сторона была чистой – Incomputable

+1

. Один из способов избежать всех этих махинаций - предоставить фиктивный первый параметр для конструктора переадресации, поэтому нет возможности путаницы. –

ответ

5

Проблема заключается в том, что first изменчиво, поэтому ссылка на него является foo& который связывается с универсальной ссылкой T&& более легко, чем const foo&.

Предположительно, вы предполагали, что T был любым классом non-foo?

В этом случае немного enable_if chicanery выражает намерение компилятора без необходимости писать нагрузку поддельных перегрузок.

#include <iostream> 

struct foo 
{ 
    foo() 
    { 
     std::cout << "def constructor is invoked\n"; 
    } 

    foo(const foo& other) 
    { 
     std::cout << "copy constructor is invoked\n"; 
    } 

    template <typename T, std::enable_if_t<not std::is_base_of<foo, std::decay_t<T>>::value>* = nullptr> 
    foo(T&& value) 
    { 
     std::cout << "templated constructor is invoked\n"; 
    } 

}; 

int main() 
{ 
    foo first; 
    foo second(first); 
    foo(6); 
} 

ожидается выход:

def constructor is invoked 
copy constructor is invoked 
templated constructor is invoked 
8

Добавить другой конструктор:

foo(foo& other) : foo(const_cast<const foo&>(other)) // for non-const lvalues 
{ 
} 

first объекта в вашем примере коде неконстантный именующий, поэтому компилятор предпочитает foo(foo&) над foo(const &). Первый предоставляется шаблоном (с T=foo&) и поэтому выбран.

Это решение включает в себя обеспечение (нешаблонном) конструктор для foo(foo&), который затем строительство делегатов конструктору копирования путем приведения его в обновление референтной к-сопзЬ

, я просто понял, что foo rvalue будет также приниматься по шаблону. Есть несколько вариантов, но я думаю, проще всего добавить делегата для foo(foo&&), аналогичный приведенному выше,

foo(foo&& other) : foo(const_cast<const foo&>(other)) // for rvalues 
{ 
} 
+0

У меня есть специализация для rvalue ref. Есть ли другой путь? Я считаю, что 'static_cast' в порядке, потому что создание объекта более const является законным. У меня уже есть много отливов, но хотелось бы посмотреть, есть ли другой способ. – Incomputable

+0

'static_cast' определенно в порядке, но я был бы обеспокоен тем, что я буду разорвать код в будущем, если я изменю тип; например, путем литья из базового типа в производный тип. Вот почему я использую 'const_cast'; просто для того, чтобы прояснить себе и компилятору, что только константа должна измениться –