неявное переинтерпретация приведения на ссылку без предупреждения / ошибки - PullRequest
0 голосов
/ 12 октября 2018

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

Проблема : Когда задействовано определение типа, можно сделать неявное переосмысление приведения, подрывая систему типов.

#include <iostream>

template<class A, class B>
inline bool
isSameObject (A const& a, B const& b)
{
  return static_cast<const void*> (&a)
      == static_cast<const void*> (&b);
}


class Wau
  {
    int i = -1;
  };

class Miau
  {
  public:
    uint u = 1;
  };


int
main (int, char**)
{
  Wau wau;
  using ID = Miau &;
  ID wuff = ID(wau);      // <<---disaster

  std::cout << "Miau=" << wuff.u
            << " ref to same object: " <<std::boolalpha<< isSameObject (wau, wuff)
            << std::endl; 
  return 0;
}

Я был шокирован, обнаружив, что gcc-4.9, gcc-6.3 и clang-3.8 принимают этот код без ошибок и выдают следующий вывод:

Miau=4294967295 ref to same object: true

Пожалуйстапримечание: я использую синтаксис конструктора типа ID(wau).Я ожидал бы такого поведения на бросках в стиле C, то есть (ID)wau.Только при использовании синтаксиса фигурных скобок нового стиля ID{wau} мы получаем ожидаемую ошибку ...

~$ g++ -std=c++11 -o aua woot.cpp

woot.cpp: In function ‘int main(int, char**)’:
woot.cpp:31:21: error: no matching function for call to ‘Miau::Miau(<brace-enclosed initializer list>)’
     ID wuff = ID{wau};
                 ^
woot.cpp:10:7: note: candidate: constexpr Miau::Miau()
 class Miau
       ^~~~
woot.cpp:10:7: note:   candidate expects 0 arguments, 1 provided
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(const Miau&)
woot.cpp:10:7: note:   no known conversion for argument 1 from ‘Wau’ to ‘const Miau&’
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(Miau&&)
woot.cpp:10:7: note:   no known conversion for argument 1 from ‘Wau’ to ‘Miau&&’

К сожалению, синтаксис фигурных скобок часто не используется в шаблонном тяжелом коде из-заstd::initializer_list фиаско.Так что для меня это серьезная проблема, так как защита системой типов здесь фактически ломается.

  • Может кто-нибудь объяснить причину такого поведения?
  • Это какая-тообратная совместимость (опять вздох)?

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Если говорить полным языком юриста, T(expression) - это преобразование результата expression в T 1 .Это преобразование имеет эффект для вызова конструктора класса 2 .Вот почему мы склонны называть неявный конструктор, принимающий ровно один аргумент, конструктор преобразования .

using ID = Miau &;
ID wuff = ID(wau);

Это эквивалентно приведенному выражению дляID.Поскольку ID не является типом класса, происходит приведение в стиле C.

Может кто-нибудь объяснить причину такого поведения?

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

Это какая-то обратная совместимость (опять вздох)?

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


1)

[expr.type.conv]

  1. A спецификатор простого типа или спецификатор typename , за которым следует необязательный список выражений в скобках или в скобках-init-list (инициализатор) создает значение указанного типа с учетом инициализатора.[...]
  2. Если инициализатор является одиночным выражением в скобках, выражение преобразования типа эквивалентно соответствующему приведенному выражению .[...]

2) (когда T имеет тип класса и такой конструктор существует)

[class.ctor]/2

Конструктор используется для инициализации объектов своего типа.Поскольку конструкторы не имеют имен, они никогда не обнаруживаются при поиске имен;однако явное преобразование типов с использованием функциональной нотации ([expr.type.conv]) вызовет конструктор для инициализации объекта.

0 голосов
/ 12 октября 2018

возможно сделать неявное переинтерпретация, подрывая систему типов.

ID wuff = ID(wau);

Это не "неявное" реинтерпретирование.Это явное преобразование типов.Хотя тот факт, что преобразование делает реинтерпретацию, действительно не так легко увидеть.В частности, синтаксис приведения называется «функциональным стилем».

Если вы не уверены, к какому типу приведения применяется явное преобразование типа (используется ли функциональный синтаксис или синтаксис в стиле C), тогда выследует воздерживаться от его использования.Многие утверждают, что явные преобразования типов никогда не должны использоваться.

Если бы вместо этого вы использовали static_cast, вы бы остались под защитой системы типов:

ID wuff = static_cast<ID>(wau);

error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'

Частотакже безопасно просто полагаться на неявные преобразования:

ID wuff = wau;

error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'

Это предполагаемое поведение

Да.

или ошибка компилятора?

Нет.

...