LINUX.ORG.RU

Шаблоны, почему не работает?

 , ,


3

9

Есть две (почти одинаковых) фунуции с шаблонным параметром:

#include <iostream>
#include <string>

template <typename String = std::string, typename Char = typename String::value_type>
void test1(const std::basic_string<Char>& s) {
  std::cout << "test1<Char> = " << s << std::endl;
}

template <typename String = std::string>
void test2(const std::basic_string<typename String::value_type>& s) {
  std::cout << "test2<typename String::value_type> = " << s << std::endl;
}

int main() {
    test1("test1");
    test2("test2");
}

Обьясните пожалуйста, почему test1 не компилируется (в то время как test2 проходит)?

P.S. g++ 6.2 говорит:

cbegin.cpp: In function ‘int main()’:
cbegin.cpp:85:18: error: no matching function for call to ‘test1(const char [6])’
     test1(«test1»);
                  ^
cbegin.cpp:73:6: note: candidate: template<class String, class Char> void test1(const std::__cxx11::basic_string<Char>&)
 void test1(const std::basic_string<Char>& s) {
      ^~~~~
cbegin.cpp:73:6: note:   template argument deduction/substitution failed:
cbegin.cpp:85:18: note:   mismatched types ‘const std::__cxx11::basic_string<Char>’ and ‘const char [6]’

в сообщении в template Char,а строчкой ниже CharType. В реальном тесте точно нет такой ошибки?

GPFault ()
Ответ на: комментарий от GPFault

Это опечатка. Должно быть одно и то же, чтобы повторить эффект.

evilface ★★ ()
[robus@ArchPortable ~]$ cat test.cpp 
#include <iostream>
#include <string>

template <typename String = std::string, typename Char = typename String::value_type>
void test1(const std::basic_string<Char>& s) {
        std::cout << "test1<Char> = " << s << std::endl;
}

template <typename String = std::string>
void test2(const std::basic_string<typename String::value_type>& s) {
        std::cout << "test2<typename String::value_type> = " << s << std::endl;
}

int main() {
        test1(std::string("test1"));
        test2("test2");
}

[robus@ArchPortable ~]$ g++ --version
g++ (GCC) 6.3.1 20170109
Copyright (C) 2016 Free Software Foundation, Inc.
Это свободно распространяемое программное обеспечение. Условия копирования
приведены в исходных текстах. Без гарантии каких-либо качеств, включая 
коммерческую ценность и применимость для каких-либо целей.

[robus@ArchPortable ~]$ g++ ./test.cpp -o ./test
[robus@ArchPortable ~]$ ./test 
test1<Char> = test1
test2<typename String::value_type> = test2

Но при этом

[robus@ArchPortable ~]$ cat test.cpp 
#include <iostream>
#include <string>

template <typename String = std::string, typename Char = typename String::value_type>
void test1(const std::basic_string<Char>& s) {
        std::cout << "test1<Char> = " << s << std::endl;
}

template <typename String = std::string>
void test2(const std::basic_string<typename String::value_type>& s) {
        std::cout << "test2<typename String::value_type> = " << s << std::endl;
}

int main() {
        test1("test1");
        test2("test2");
}

[robus@ArchPortable ~]$ g++ ./test.cpp -o ./test
./test.cpp: В функции «int main()»:
./test.cpp:15:15: ошибка: нет соответствующей функции для вызова «test1(const char [6])»
  test1("test1");
               ^
./test.cpp:5:6: замечание: candidate: template<class String, class Char> void test1(const std::__cxx11::basic_string<Char>&)
 void test1(const std::basic_string<Char>& s) {
      ^~~~~
./test.cpp:5:6: замечание:   вывод/подстановка аргумента шаблона неудачна:
./test.cpp:15:15: замечание:   несоответствие типов «const std::__cxx11::basic_string<Char>» и «const char [6]»
  test1("test1");
               ^

Т.е. аргумент имеет тип const char*, а не std::string, в результате фейлится дедукция типа шаблона.

robus ★★★★ ()
Последнее исправление: robus (всего исправлений: 1)
Ответ на: комментарий от robus

Гораздо больше интересно, почему она фейлится. Фейл-то очевиден. Ну, лично я ради именно этого знания на тему подписался.

evilface ★★ ()
Ответ на: комментарий от robus

Т.е. аргумент имеет тип const char*, а не std::string, в результате фейлится дедукция типа шаблона.

Так интересно, потому, что у basic_string<CharT> есть конструктор c сигнатурой

basic_string( const CharT* s,
              const Allocator& alloc = Allocator() );

... в test2 он какраз и вызывается.

KennyMinigun ★★★★★ ()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от evilface

Кажется я понял куда копать: template deduction не работает для «вложенных типов»:http://en.cppreference.com/w/cpp/language/template_argument_deduction

Non-deduced contexts
1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

// the identity template, often used to exclude specific arguments from deduction
template<typename T> struct identity { typedef T type; };
template<typename T> void bad(std::vector<T> x, T value = 1);
template<typename T> void good(std::vector<T> x, typename identity<T>::type value = 1);
std::vector<std::complex<double>> x;
bad(x, 1.2);  // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
              // P1/A1: deduced T = std::complex<double>
              // P2 = T, A2 = double
              // P2/A2: deduced T = double
              // error: deduction fails, T is ambiguous
good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
              // P1/A1: deduced T = std::complex<double>
              // P2 = identity<T>::type, A2 = double
              // P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2
              // OK: T = std::complex<double>

... дедукция typename Char не происходит, т.к. он вложен в String и зависит от определеня String. Однако я еще не до конца не понимаю, почему параметр по умолчанию игнорируется (возможно из-за того, что была проба дедукции?).

KennyMinigun ★★★★★ ()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от KennyMinigun

Так интересно, потому, что у basic_string<CharT> есть конструктор c сигнатурой

А при дедукции компилятор разве смотрит на наличие конструкторов?

Вот так кстати работает

#include <iostream>
#include <string>

template<typename T> struct identity { typedef T type; };

template <typename String = std::string, typename Char = typename String::value_type>
void test1(const std::basic_string<typename identity<Char>::type>& s) {
  std::cout << "test1<Char> = " << s << std::endl;
}

int main() {
    test1("test1");
}
anatoly ()
Ответ на: комментарий от anatoly

А при дедукции компилятор разве смотрит на наличие конструкторов?

Неа, но меня сбило с толку сообщение об ошибке:

mismatched types ‘const std::__cxx11::basic_string<Char>’ and ‘const char [6]’

(наверное мне стоило посмотреть на строчку выше :))

А в примере s исключается из дедукции и компилятор берет значение по умолчанию.

KennyMinigun ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.