9

Скажем, у меня есть класс FunctionWrapper, определенный как это:Запретить неявное преобразование, но разрешить инициализацию списка?

struct FunctionWrapper 
{ 
    FunctionWrapper(std::function<void()> f); 

    // ... plus other members irrelevant to the question 
}; 

Я хотел бы, чтобы предотвратить неявные преобразования из std::function<void()> в FunctionWrapper, но позволяют построения FunctionWrapper с помощью бандажа инициализации синтаксиса (то есть, используя список инициализации с одним аргументом). Другими словами, мне бы хотелось:

void foo(); 
void wrap(FunctionWrapper); 

wrap(foo); // (1) error 
wrap({foo}); // (2) OK 
wrap(FunctionWrapper{foo}); // (3) OK 

Есть ли способ достичь этого? То, как я определил класс выше, не так: это позволяет неявные преобразования, поэтому (1) компилируется.

Если добавить explicit конструктору:

struct FunctionWrapper 
{ 
    explicit FunctionWrapper(std::function<void()> f); 

    // ... plus other members irrelevant to the question 
}; 

это не помогает, так как это идет «слишком далеко» и Запрещает (2), а также (1).

Есть ли способ достичь «средней земли» и скомпилировать (2), пока (1) выдает ошибку?

+4

Добавить явный конструктор, который принимает параметр 'std :: initializer_list'. –

+3

вы можете использовать initializer_list, но тогда у вас будет только проверка времени на количество аргументов ... может быть, шаблон принимает const-массив и static_assert на размер шаблона ... но это просто уродливо – Hcorg

+0

@Hcorg не будет [это] (http: /melpon.org/wandbox/permlink/0rFgVYD8XKYwVlC1) требуется инициализация двойной скобки? –

ответ

8

Есть ли способ достичь этого?

Да. У вас уже есть это.

wrap(foo); 

Для этого, чтобы работать, он будет включать в себя два определяемые пользователем преобразования: void(*)() --> std::function<void()> --> FunctionWrapper, но мы только разрешено до одного пользовательского преобразования. Таким образом, это ошибка и будет, если вы не добавите отдельный конструктор в FunctionWrapper, чтобы разрешить это.

wrap({foo}); 

Это уже хорошо, мы копирование списка инициализация FunctionWrapper поэтому указанное выше ограничение не применяется.

wrap(FunctionWrapper{foo}); 

Это, безусловно, хорошо.


Обратите внимание, что это также обеспечивает путь вперед для тех случаев, когда ваш первый пример действительно работал. Скажем, у вас были:

struct Wrapper { 
    Wrapper(int) { } 
}; 

foo(0);   // want this to fail 
foo({0});   // want this to be OK 
foo(Wrapper{0}); // ... and this 

Вы не можете сделать конструктор explicit, так что вызывает foo({0}) на провал, а также. Но вы можете просто добавить еще один слой косвенности с другой обертке:

struct AnotherWrapper { 
    AnotherWrapper(int i): i{i} { } 
    int i; 
}; 

struct Wrapper { 
    Wrapper(AnotherWrapper) { } 
}; 

Здесь wrap(0) терпит неудачу, но wrap({0}) и wrap(Wrapper{0}) исправны.