Bị C++ template đưa vào thế bí?

Mình có code như sau:

#include <vector>
using namespace std;
template<class Elem, template <class> class List>
void test(const List<Elem>& arr, const Elem& a) {}
int main() {
   test(vector<int>(), 8);
}

Trình biên dịch báo lỗi no instance of function template “test” matches the argument list.
Nhưng mình viết thế này thì ok:

#include <vector>
using namespace std;
template<class Elem, template <class> class List, class Elem2>
void test(const List<Elem>& arr, const Elem2& a) {}
int main() {
   test(vector<int>(), 8);
}

Chuyện trở nên vô cùng rắc rối nếu mình viết thế này, có chia ra Elem2 cũng không giúp được:

#include <vector>
#include <string>
using namespace std;
template<class Elem, template <class> class List>
void test(const List<basic_string<Elem>>& arr, const basic_string<Elem>& a) {}
int main() {
   test(vector<string>(), string());
}

Có cách nào chỉ dùng 1 Elem thôi mà vẫn build được không?

Có lẽ bạn dùng compiler cũ nên nó chưa hỗ trợ toàn bộ tính năng của C++ 17 (đơn cử như gnu/gcc 6.x nó release date trước ngày release C++17 official).
(Với C++ 17 thôi nhé)

C++17 có tính năng gì cho vấn đề này vậy?

Mình cũng không rõ vì bản thân mình không đi sâu vào template, từ ngày dùng template là mình đã dùng gcc/gnu 7.2 rồi.

Bạn có thể đọc feature của C++17 để xem các thay đổi.
Ref: http://www.bfilipek.com/2017/01/cpp17features.html

Vậy là phải ngồi update Visual Studio rồi :worried:

Xài MingW,msys2 hoặc đổi compiler của VS mình thấy thoải mái hơn

#include <vector> using namespace std; template<class Elem, template <class> class List> void test(const List<Elem>& arr, const Elem& a) {} int main() { test(vector<int>(), 8); }

Cái std::vector<int>() của bạn chỉ được xem như là 1 type T thôi (phiên bản int của std::vector, nên chỉ cần template<class T> là đủ, template <class> class List là quá thừa thãi (và sai).

template<class T> void test(const T &t) {}
int main()
{
    test(std::vector<int>());
}

template <class T, typename U = float>
void test(const T &x, U u) {}

int main()
{
    test(std::vector<int>(), 8.0f);
    test(std::vector<int>(), 16u);
    test(std::vector<int>(), 24L);
}

Edit: Nếu argument là primitive type (float, double, int…) thì hạn chế dùng const T &.

Vậy có cách nào để ràng buộc 2 typename đó không, hay chỉ cần viết template <class T, typename U> rồi mặc kệ? Nhu cầu của mình là sử dụng basic_string làm Elem.

template<class CharType, template <class> class List>
void test(const List<basic_string<CharType>>& arr, const basic_string<CharType>& a) {}

Đoạn code với basic_string bạn đưa ra ở trên là đúng (như @Dark.Hades đã nói, nó chạy được trên C++17). Đọc trên mạng họ bảo cú pháp này sẽ sai trước C++17, chính xác thì nó nằm ở đây:

Giải quyết thì bạn có thể dùng C++17, khi đó code sẽ đúng, hoặc nếu dùng các standard cũ hơn C++17 thì phải tìm cách thay thế. Tạm thời mình nghĩ không ra :joy: nhưng nếu không quan trọng vấn đề container nào thì bạn có thể nhét luôn std::vector vào test cho đơn giản:

#include <vector>
#include <string>
using namespace std;
template<class Elem>
void test(const std::vector<basic_string<Elem>>& arr, const basic_string<Elem>& a) {}
int main() {
   test(vector<string>(), string());
}
1 Like

http://en.cppreference.com/w/cpp/container/stack

template<
    class T,
    class Container = std::deque<T>
> class stack;

cái template này giống stack hay queue
thử gọi
std::stack<std::string, std::list<int>> s;
compile được, nhưng gọi
s.push("abc"); thì lại bị lỗi, còn gọi
s.push(11); lại compile được. @_@

cũ hơn C++17 thì có thể xài static_assert, nhưng mà lúc báo lỗi nó ko trỏ tới dòng lệnh gây ra lỗi :joy:

template <class T, class Container=std::vector<T>>
void test(Container const& p, T const& t)
{
    static_assert(std::is_same<typename Container::value_type, T>::value,
                  "Container::value_type must be T");
}

ví dụ như trên thì lúc báo lỗi nó nhảy tới dòng static_assert này chứ ko nhảy tới dòng gây ra lỗi, nhưng có thể truy ra được, được cái nó in ra cái lý do rõ ràng, ở đây là “Container::value_type must be T”

để xài với std::string thì xài string literals
vd:

std::vector<std::string> p;
test(p, "abc");

sẽ gây ra lỗi:

source_file.cpp: In instantiation of ‘void test(const Container&, T) [with T = const char*; Container = std::vector<std::__cxx11::basic_string<char> >]’:
source_file.cpp:21:18:   required from here
source_file.cpp:14:5: error: static assertion failed: Container::value_type must be T
     static_assert(std::is_same<typename Container::value_type, T>::value,
     ^

vì “abc” được compiler hiểu là const char* chứ ko phải std::string. Sử dụng thêm namespace literals::string_literals là được:

using namespace std::literals::string_literals;
std::vector<std::string> p;
test(p, "abc"s); //có chữ s phía sau để trình dịch tự động chuyển "abc" thành std::string
4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?