Как избежать «неявного» вызова однопараметрического конструктора в std :: pair - PullRequest
17 голосов
/ 15 ноября 2011

Первоначальная проблема заключалась в том, как безопасно работать с std::map<std::wstring, std::wstring> >, поскольку равные типы ключа и значения чрезвычайно подвержены ошибкам.Поэтому я решил создать простую оболочку для значения:

    struct ComponentName
    {
      std::wstring name;

      // I want to prohibit any implicit string-ComponentName conversions!!!
      explicit ComponentName(const std::wstring& _name) : name(_name)
      {
      }

      bool operator<(const ComponentName& item_to_compare) const
      {
        return name < item_to_compare.name;
      }
    };

    typedef std::map<std::wstring, ComponentName> component_names_map;

Но следующий код работает хорошо!

component_names_map component_names;
// Are you sure that ComponentName's constructor cannot be called implicitly? ;)
component_names_map::value_type a_pair = std::make_pair(L"Foo", L"Bar");

Он работает, потому что конструктор копирования std::pair<std::wstring, ComponentName> явно использует строкуконструктор ComponentName для назначения экземпляра std::pair<std::wstring, std::wstring>.Это абсолютно легальная операция.Однако это выглядит как «неявный» вызов конструктора ComponentName.

Итак, я знаю причину проблемы, но как мне избежать этого «неявного» преобразования wstring-ComponentName?Самый простой способ - не объявлять строковый конструктор, но это делает инициализацию ComponentName неудобной.

Ответы [ 3 ]

10 голосов
/ 15 ноября 2011

Я думаю, что вы можете легально сделать это, добавив частичную специализацию std::pair для вашего типа:

namespace std {
    template <typename T>
    struct pair<T,ComponentName> {
       typedef T first_type;
       typedef ComponentName second_type;

       T first;
       ComponentName second;
       // The rest of the pair members:
       // ....
       // Any trick you like to make it fail with assignment/construction from 
       // pair<std::wstring, std::wstring>
    };
}

Обоснование:

В п. 17.6.4.2.1 изложены основныеправила для специализаций в пространстве имен std:

"Программа может добавить специализацию шаблона для любого стандартного шаблона библиотеки в пространство имен std, только если объявление зависит от типа, определенного пользователем, и специализация соответствуеттребования стандартной библиотеки для исходного шаблона и не запрещены явно "

Я не вижу явного запрета, который исключил бы этот конкретный случай, если вы заполнили остальную часть класса в пределахграницы § 20.3.


Альтернативный, возможно, правовой подход:

Специализировать std::is_constructible<ComponentName, std::wstring> таким, что value является ложным.Это указано как требование как оператора присваивания, так и конструктора копирования для std::pair s различных типов.Я не вижу никаких запретов на быстрое сканирование этого, но я не могу найти ничего, говорящего, что реализации необходимы , чтобы проверить требования.

4 голосов
/ 15 ноября 2011

Проблема (в C ++ 03) состоит в том, что большинство реализаций стандартной библиотеки не соответствуют стандарту.В частности, стандарт гласит, что когда std::pair<T,U> строится из другого std::pair<V,W>, члены создаются неявными преобразованиями.Проблема в том, что на самом деле очень трудно (если вообще возможно) ограничить это преобразование в реализации шаблонного конструктора pair, поэтому в текущих реализациях выполняется явное преобразование аргументов:

template <typename T, typename U>
struct pair {
    // ...
    template <typename V, typename W>
    pair( pair<V,W> const & p ) : first( p.first ), second( p.second ) {} 
};

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

Примечание: в C ++ 11 (§20.3.2p12-14) это неявное преобразование также запрещено (из FDIS):

template<class U, class V> pair(pair<U, V>&& p);

Требуется: is_constructible :: value - true и is_constructible :: value - true.

Эффекты: конструктор инициализирует сначала std :: forward (p.first) и второйс помощью std :: forward (p.second).

Примечание. Этот конструктор не должен участвовать в разрешении перегрузки, если только U неявно преобразуется в first_type, а V неявно преобразуется в second_type.

Эквивалентные ограничения присутствуют в p9-11 дляэквивалент для template<class U, class V> pair(const pair<U, V>& p); (в случае, если типы не подвижные )

3 голосов
/ 15 ноября 2011

Simple:

enum FromString { fromString };

ComponentName( FromString, std::wstring const& aName)
    : name( aName )
{}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...