2016-03-30 2 views
7

Я пытался понять использование явного ключевого слова в C++ и посмотрел на этот вопрос на SO What does the explicit keyword mean in C++?использование явного ключевого слова конструкторами

Однако примеры, приведенные там (на самом деле обе верхние два ответа) не очень понятно относительно использования. Например,

// classes example 
#include <iostream> 
using namespace std; 

class String { 
public: 
    explicit String(int n); // allocate n bytes to the String object 
    String(const char *p); // initializes object with char *p 
}; 

String::String(int n) 
{ 
    cout<<"Entered int section"; 
} 

String::String(const char *p) 
{ 
    cout<<"Entered char section"; 
} 

int main() { 

    String mystring('x'); 
    return 0; 
} 

Теперь я объявил строка конструктор, как явное, но позволяет сказать, если я не перечислить его как явное, если я называю конструктор как,

String mystring('x'); 

ИЛИ

String mystring = 'x'; 

В обоих случаях я войду в секцию int. До тех пор, пока я не укажу тип значения, значение по умолчанию будет равно int. Даже если я получить более конкретные аргументы, как объявить один, как межд, другие, как двойные, а не использовать явно с именем конструктора и назвать его таким образом

String mystring(2.5); 

Или этот путь

String mystring = 2.5; 

Это будет всегда по умолчанию конструктор с двойным аргументом. Итак, мне трудно понять реальное использование явного. Можете ли вы привести мне пример, где не использование явного будет реальным провалом?

+1

Ожидаете ли вы 'String ('x')' для вызова 'String (const char *)'? '' x'' является символом 'char', а не' char'. Во всяком случае, вы только показываете * явные * конструкции 'String', поэтому' explicit' не имеет значения. Попробуйте вызвать функцию, которая принимает 'String' с' int', и она не будет работать, потому что преобразование 'int' →' String' не может быть сделано * неявно *. – Biffen

+0

'String mystring = 'x';' [does _not_ work] (http://coliru.stacked-crooked.com/a/79af8ce27adf8a61). – cpplearner

+0

@cpplearner Я знаю об этом, поэтому я указал, что я не использую это слово явно. В вашем примере вы используете явно –

ответ

6

explicit предназначен для предотвращения переходов неявных. Каждый раз, когда вы используете что-то вроде String(foo);, это явное преобразование, поэтому использование explicit не изменит, удастся ли оно или не удастся.

Итак, давайте рассмотрим сценарий, который подразумевает неявное преобразование. Давайте начнем с вашего String класса:

class String { 
public: 
    explicit String(int n); // allocate n bytes to the String object 
    String(const char *p); // initializes object with char *p 
}; 

Тогда давайте определим функцию, которая принимает параметр типа String (также может быть String const &, но String будет делать на данный момент):

int f(String); 

Ваши Конструкторы позволяют неявное преобразование от char const *, но только явное преобразование от int. Это означает, что, если я позвоню:

f("this is a string"); 

... компилятор будет генерировать код для создания объекта String, из строкового литерала, а затем вызвать f с этим String объекта.

Если, однако, вы пытаетесь вызвать:

f(2); 

Он потерпит неудачу, потому что String конструктор, который принимает int параметр был отмечен explicit.Это означает, что, если я хочу, чтобы преобразовать int в String, я должен сделать это явно:

f(String(2)); 

Если String(char const *); конструктор был также отмечен explicit, то вы бы не быть в состоянии назвать f("this is a string") либо - вам 'd необходимо использовать f(String("this is a string"));

Обратите внимание, что explicit только контролирует неявное преобразование из определенного типа foo в тип, который вы определили. Он не влияет на неявное преобразование из другого типа в типа вашего explicit. Таким образом, ваш явный конструктор, который принимает тип int будет еще параметр с плавающей точкой:

f(String(1.2)) 

... потому что предполагает неявное преобразование из double в int с последующим явным приведением из int в String. Если вы хотите запретить преобразование из double в String, вы бы сделать это (например) обеспечение перегруженного конструктора, который принимает double, но потом бросаешь:

String(double) { throw("Conversion from double not allowed"); } 

Теперь неявное преобразование из double в int не произойдет - double будет передаваться непосредственно на ваш ctor без преобразования.

Как к тому, что с помощью explicit выполняет: основной смысл использования explicit является предотвратить кода при компиляции, которые могли бы составить. В сочетании с перегрузкой неявные преобразования могут иногда приводить к некоторым довольно странным выборам.

Проще показать проблему с операторами преобразования, а не с конструкторами (потому что вы можете сделать это только с одним классом). Например, давайте рассмотрим маленький класс строки очень похож на много, которые были написаны, прежде чем люди поняли, как много о том, как проблематично неявное преобразование может быть:

class Foo { 
    std::string data; 
public: 
    Foo(char const *s) : data(s) { } 
    Foo operator+(Foo const &other) { return (data + other.data).c_str(); } 

    operator char const *() { return data.c_str(); } 
}; 

(я изменял с помощью std::string для хранения данных, но то же самое было бы верно, если бы я сделал это, и сохранил char *, и использовал new для выделения памяти).

Теперь это делает вещи, как эта работа штрафа:

Foo a("a"); 
Foo b("b"); 

std::cout << a + b; 

... и (конечно же) результат в том, что он печатает ab. Но что произойдет, если пользователь допустит небольшую ошибку и типа -, где они намеревались ввести +?

Вот где вещи становятся уродливыми - код все еще компилируется и «работает» (для некоторого определения слова), но печатает глупости. В ходе быстрого теста на моей машине я получил -24, но не рассчитываю на дублирование этого конкретного результата.

Проблема здесь связана с допущением неявного преобразования из String в char *. Когда мы пытаемся вычесть два объекта String, компилятор пытается понять, что мы имели в виду.Поскольку он не может вычитать их напрямую, он смотрит, может ли он преобразовать их в какой-то тип, который поддерживает вычитание, и, конечно же, char const * поддерживает вычитание, поэтому он преобразует оба наших объекта String в char const *, а затем вычитает два указателя.

Если мы отмечаем, что преобразование как explicit вместо:

explicit operator char const *() { return data.c_str(); } 

... код, который пытается вычесть два String объекты просто не скомпилируется.

Эта же основная идея может быть применима к explicit конструкторам, но код для демонстрации ее становится длиннее, потому что нам обычно требуется как минимум несколько различных классов.

+0

Я не уверен, что вы верны при переходе от сомнений к int. Если я передам двойное значение объекту String, он будет напрямую перейти к конструктору, который ожидает double как аргумент (если он есть), независимо от явного. –

+0

@UnderDog: Да и нет. Да, это одно разрешение перегрузки. Но, если он помечен как явный, и вы пытаетесь использовать его для неявного преобразования, тогда код не будет компилироваться. –

+0

Я это понимаю и согласен. Но мой вопрос не в этом. Мне больше интересно узнать о случаях или примерном коде, где не используются явные причины сбоя или нежелательного поведения, потому что до сих пор с объяснением, которое я понял, я не вижу большой ценности при использовании явного. –

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