Семантика для обернутых объектов: ссылка / значение по умолчанию через std :: move / std :: ref

В последнее время я часто использую естественную идиому "обнаружил» в C ++ 11 это означает, что обернутый объект может автоматически содержать ссылку, когда это возможно. Главный вопрос здесь будет о сравнении с этим поведением »идиома» к другому поведению в стандарте (см. ниже).

Например:

template
struct wrap{
    T t;
};
template wrap make_wrap(T&& t){
    return wrap{std::forward(t)};
}

Таким образом, для кода

double a = 3.14
double const c = 3.14

Я получил,

typeid( make_wrap(3.14) ) --> wrap
typeid( make_wrap(a) ) --> wrap
typeid( make_wrap(c) ) --> wrap

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

typeid( make_wrap(std::move(a)) ) --> wrap // noref
typeid( make_wrap(std::move(c)) ) --> wrap // noref

Таким образом, это поведение кажется естественным в C ++ 11.

Затем я вернулся кstd::pair а такжеstd::make_pair и как-то я ожидал, что они использовали это новое, казалось бы, естественное поведение, но, видимо, поведение "более традиционный, Так, например:

typeid( std::make_pair(3.14, 3.14) ) --> std::pair
typeid( std::make_pair(a, a) ) --> std::pair // noref
typeid( std::make_pair(c, c) ) --> std::pair // noref

а такжеза Рекомендации:

typeid( std::make_pair(std::ref(a), std::ref(a) ) ) --> std::pair // ref
typeid( std::make_pair(std::ref(c), std::ref(c) ) ) --> std::pair // const ref

Это задокументировано здесь:http://en.cppreference.com/w/cpp/utility/pair/make_pair

Как видите, два поведения "напротив", в некотором смыслеstd::ref является дополнением кstd::move, Таким образом, оба поведения одинаково гибки в конце, но мне кажется, чтоstd::make_pair поведение сложнее реализовать и поддерживать.

Вопрос в том:Является ли текущее поведениеstd::make_pair отменить ссылки по умолчанию просто проблема обратной совместимости? потому что какое-то историческое ожидание? или есть более глубокая причина, которая все еще существует в C ++ 11?

Как это выглядит такstd::make_pair поведение гораздо сложнее реализовать, так как требует специализации для (std::refstd::reference_wrapper) а такжеstd::decay и даже кажется неестественным (при наличииC ++ 11 Move "). В то же время, даже если я решу продолжать использовать первое поведение, я боюсь, что поведение будет довольно неожиданным по отношению к текущим стандартам, даже в C ++ 11.

На самом деле, я очень люблю первое поведение, до такой степени, что элегантное решение может изменить префиксmake_something за что-то вродеconstruct_something чтобы отметить разницу в поведении. (РЕДАКТИРОВАТЬ: один из комментариев предложил посмотреть наstd::forward_as_tupleтак что другое название соглашения может бытьforward_as_something). Что касается именования, ситуация не является четкой, когда передача по значению, передача по ссылке смешивается при построении объекта.

EDIT2: Это редактирование просто чтобы ответить @Yakk 'о возможности "копия» обернуть объект с различными свойствами ref / value. Это не часть вопроса, а всего лишь экспериментальный код:

template
struct wrap{
    T t;
    // "generalized constructor"? // I could be overlooking something important here
    template wrap(wrap const& w) : t(std::move(w.t)){}
    wrap(T&& t_) : t(std::move(t)){} // unfortunately I now have to define an explicit constructor
};

Это, кажется, позволяет мне копировать между несвязанными типамиwrap а также :wrap

auto mw = make_wrap(a);
wrap copy0 =mw;
wrap copy1 = mw; //would be an error if `a` is `const double`, ok
wrap copy2 = mw;

EDIT3: Это изменение должно добавить конкретный пример, в котором традиционный вывод ссылки может потерпеть неудачу, зависит от "протокол», Пример основан на использовании Boost.Fusion.

Я обнаружил, насколько неявное преобразование из ссылки в значение может зависеть от соглашения. Например, старый добрый Boost.Fusion следует соглашению STL

Fusion»Функции генерации (например, make_list) по умолчанию сохраняют типы элементов в виде простых не ссылочных типов.

Однако это зависит от точноготип" что тег ссылки, в случае с Fusion былboost::ref и в случаеmake_pair является...std::refсовершенно не связанный класс. Итак, в настоящее время, учитывая

double a;

типboost::fusion::make_vector(5., a ) являетсяboost::fusion::vector2, Хорошо.

И типboost::fusion::make_vector(5., boost::ref(a) ) ) isповышение :: фьюжн :: vector2`. Хорошо, как задокументировано.

Однако, удивительно, поскольку Boost.Fusion не был написан на C ++ 11 STL 'Имея в виду, мы получаем:boost::fusion::make_vector(5., std::ref(a) ) ) имеет типboost::fusion::vector2, Сюрприз!

Этот раздел должен был показать, что текущее поведение STL зависит от протокола (например, какой класс использовать для маркировки ссылок), а другой (что я назвал)естественно» поведение) с помощьюstd::move (или, точнее, приведение к значению)зависит от протокола, но он более родной для (текущего) языка.

Ответы на вопрос(0)

Ваш ответ на вопрос