Явное ключевое слово применяется к оператору вместо конструктора - PullRequest
0 голосов
/ 24 сентября 2018

В приведенном ниже классе

Зачем вам делать операторы explicit.Я думал, что explicit должен был предотвратить неявный вызов конструкторов?

 class Content
            {
public:

 virtual ~Content() = 0;
 virtual explicit operator float&();
 virtual explicit operator long long&();
 virtual explicit operator std::string&()
}

Ответы [ 4 ]

0 голосов
/ 24 сентября 2018

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

Умный указатель обычно имеет преобразование в bool, поэтому вы можете сделать это:

std::shared_ptr<int> foo;
if (foo) {
  *foo = 7;
}

, где if(foo) преобразует foo в bool.К сожалению:

int x = foo+2;

преобразует foo в bool, затем в int, затем добавляет 2.Это почти всегда ошибка.Это разрешено, потому что, хотя выполняется только одно пользовательское преобразование, пользовательское преобразование, за которым следует встроенное преобразование, может произойти в молчании.

Чтобы исправить это, программисты должны делать сумасшедшие вещи, такие как add:

struct secret {
  void unused();
};
struct smart_ptr {
  using secret_mem_ptr = void(secret::*)();
  operator secret_mem_ptr() const { return 0; }
};

и secret_mem_ptr - это секретный указатель на член.Указатель на член имеет встроенное преобразование в bool, поэтому:

smart_ptr ptr;
if (!ptr) {
}

"работает" - ptr преобразуется в secret_mem_ptr, который затем преобразуется в bool, чтозатем используется, чтобы решить, какую ветвь взять.

Это было больше чем хак.

Они добавили explicit в операторы преобразования для решения этой конкретной проблемы.

Сейчас:

struct smart_ptr {
  explicit operator bool() const { return true; }
};

не разрешает:

smart_ptr ptr;
int x = 3 + ptr;

, но разрешает:

if (ptr) {
}

, потому что правила были созданы вручную для поддержкиименно этот вариант использования.Это также не разрешает:

bool test() {
  smart_ptr ptr;
  return ptr;
}

здесь, вы должны ввести:

bool test() {
  smart_ptr ptr;
  return (bool)ptr;
}

, где вы явно конвертируете ptr в bool.

(Я обычно действительно против бросков в стиле C; я делаю исключение в случае (bool)).

0 голосов
/ 24 сентября 2018

Примите во внимание следующее:

struct Content
{
    operator float() { return 42.f; }  
    friend Content operator+(Content& lhs, float) { return lhs; }
};

int main()
{
    Content c{};
    c + 0; //  error: ambiguous overload for 'operator+'
}

Здесь компилятор не может выбирать между operator+(Content&, float) и operator+(float, int).Если оператор float explicit разрешит эту неоднозначность *:

c + 0; // operator+(Content&, float)

или

static_cast<float>(c) + 0; // operator+(float, int)

*), при условии, что имеет смысл отдавать предпочтение одному над другим.

0 голосов
/ 24 сентября 2018

Я думал, что явное было для предотвращения неявного вызова конструкторов?

Начиная с C ++ 11 это также относится к определяемым пользователем преобразованиям (он же оператор приведения ).

Зачем вам делать операторы explicit

Используемый в этом контексте explicit ключевое слово делает преобразование приемлемым только для прямой инициализации и явных преобразований .Смотрите здесь в [class.conv.fct¶2] :

Функция преобразования может быть явной ([dcl.fct.spec]), в этом случае он рассматривается только как пользовательское преобразование для прямой инициализации ([dcl.init]).В противном случае определяемые пользователем преобразования не ограничиваются использованием в присваиваниях и инициализациях.

Это помогает вам убедиться, что компилятор не пытается выполнить преобразование вопреки вашим намерениям, так что вам придется явно приводитьСам, оставляя меньше места для ошибок.Пример:

struct Foo
{
    explicit operator int() {return 0;}
    operator int*() {return nullptr;}
};

int main()
{
    Foo foo;

    //int xi = foo; // Error, conversion must be explicit
    int i = static_cast<int>(foo); // OK, conversion is explicit
    int* i_ptr = foo; // OK, implicit conversion to `int*` is allowed

    int i_direct(foo); // OK, direct initialization allowed
    int* i_ptr_direct(foo); // OK, direct initialization after implicit conversion

    return 0;
}

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

struct Bar
{
    operator int() {return 1;}
    operator char() {return '1';}
};

int main()
{
    Bar bar;    
    //double d = bar; // Error, implicit conversion is ambiguous    
    return 0;
}

Добавить explicit:

struct Bar
{
    operator int() {return 1;}
    explicit operator char() {return '1';}
};

int main()
{
    Bar bar;    
    double d = bar; // OK, implicit conversion to `int` is the only option    
    return 0;
}
0 голосов
/ 24 сентября 2018

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

 void f( float f );
 ....
 Content c;
 f( c );      // conversion from Content to float

Без квалификатора explicit преобразование происходит неявно;с ним вы получите ошибку компиляции.

Бесшумные, неявные преобразования могут стать источником путаницы и / или ошибок, поэтому обычно лучше сделать операторы явными или, возможно, еще лучше предоставить именованные функции, такие как ToFloat, которыерасскажите читателю, что именно происходит.

...