C ++ 11 make_pair із зазначеними параметрами шаблону не компілюється


84

Я просто бавився з g ++ 4.7 (одним із пізніших знімків) з увімкненим -std = c ++ 11. Я намагався скомпілювати деякі мої існуючі основи коду, і один випадок, який не вдався, дещо бентежить мене.

Буду вдячний, якщо хтось зможе пояснити, що відбувається.

Ось код:

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

Я розумію, що make_pair призначений для використання як випадок (1) (якщо я вкажу типи, тоді я також можу використовувати (3)), але я не розумію, чому в цьому випадку він не працює.

Точна помилка:

test.cpp: In function ‘int main()’:
    test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

Знову ж таки, питання тут просто "що відбувається?" Я знаю, що можу вирішити проблему, видаливши специфікацію шаблону, але я просто хочу знати, що тут не виходить під ковдрами.

  • g ++ 4.4 без проблем компілює цей код.
  • Видалення -std = c ++ 11 також компілюється з кодом без проблем.

6
Відмінне запитання. Ще один приклад тонкої розривної зміни в C ++ 11, подібно до розривної зміни в std::vectorконструкції . Принаймні, це дає помилку компілятора, а не тиху зміну семантики.
Джеймс МакНелліс,

1
Якщо у мене є ціла змінна i. Я хочу створити пару з i та іншим об'єктом. Як саме я повинен назвати makepair. 1) make_pair <* i, obj> 2) int && j = i; make_pair <j, obj>? Обидва не працюють. Як правильно це зробити?
PHcoDer

Відповіді:


135

Це не те, як std::make_pairпередбачається використовувати; ви не повинні чітко вказувати аргументи шаблону.

C ++ 11 std::make_pairприймає два аргументи, типу T&&і U&&, де Tі Uє параметрами типу шаблону. Фактично, це виглядає так (ігноруючи тип повернення):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

Коли ви викликаєте std::make_pairта явно вказуєте аргументи типу шаблону, відрахування аргументів не відбувається. Натомість аргументи типу підставляються безпосередньо в декларацію шаблону, отримуючи:

[return type] make_pair(std::string&& argT, int&& argU);

Зверніть увагу, що обидва ці типи параметрів є посиланнями rvalue. Таким чином, вони можуть прив'язуватися лише до rзначень. Це не проблема для другого аргументу, який ви 7передаєте, оскільки це вираз rvalue. s, однак, це вираз lvalue (він не є тимчасовим і не переміщується). Це означає, що шаблон функції не відповідає вашим аргументам, саме тому ви отримуєте помилку.

Отже, чому це працює , якщо ви явно не вказати , що Tі Uв списку аргументів шаблону? Коротше кажучи, посилальні параметри rvalue є особливими у шаблонах. Частково завдяки мовній функції, яка називається згортанням посилань , посилальний параметр rvalue типу A&&, де Aє параметром типу шаблону, може прив'язуватися до будь-якого типу A.

Не має значення, Aє значення lvalue, rvalue, const-qualified, volatile-kvalified, або некваліфіковане, an A&&може прив'язуватися до цього об'єкта (знову ж таки, якщо і тільки якщо Aсам є параметром шаблону).

У вашому прикладі ми здійснюємо дзвінок:

make_pair(s, 7)

Тут, sє значення l std::stringі 7є значенням r int. Оскільки ви не вказуєте аргументи шаблону для шаблону функції, виконується вирахування аргументу шаблону, щоб з'ясувати, якими є аргументи.

Для прив'язки s, значення l, до T&&, компілятор виводить Tяк std::string&, даючи аргумент типу std::string& &&. Однак посилань на посилання немає, тому це "подвійне посилання" згортається std::string&. s- це сірник.

Це просто , щоб пов'язувати 7з U&&: компілятор може вивести Uбути int, що дає параметр типу int&&, який успішно зв'язується , 7тому що це Rvalue.

У цих нових мовних можливостей багато тонкощів, але якщо дотримуватися одного простого правила, це досить просто:

Якщо аргумент шаблону можна вивести з аргументів функції, нехай він буде виведений. Не вказуйте явно аргументи, якщо це не потрібно.

Нехай компілятор виконує важку роботу, і в 99,9% випадків він все одно буде саме тим, що ти хотів. Коли це не те, що ви хотіли, ви зазвичай отримуєте помилку компіляції, яку легко виявити та виправити.


6
Це дуже гарне та вичерпне пояснення. Дякую!
vmpstr

1
@James - це "одне просте правило" з іншої статті чи відповіді, яку я повинен прочитати?
Michael Burr

4
@MichaelBurr: Ні, я щойно це вигадав. :-) Отже, сподіваюся, це правда! Я думаю, що це правда ... це правило працює для мене майже весь час.
Джеймс МакНелліс,

1
@James: спасибі. "Поле з цитатами" навколо нього змусило мене думати, що це могло бути щось спочатку написане в іншому місці. Ця відповідь була справді інформативною, і я просто хотів переконатись, що не пропускаю чогось ще.
Майкл Берр,

2
Це стосується і кортежів?
Ферруччо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.