2013-08-03 33 views
7

Tôi đang cố gắng tìm một cách thoải mái để chuyển các chuỗi ký tự dạng chuỗi làm đối số mẫu. Tôi không quan tâm đến việc hỗ trợ số lượng trình biên dịch có thể lớn nhất, tôi đang sử dụng phiên bản g ++ mới nhất với --std=c++0x.Cố gắng chuyển các chuỗi ký tự dạng chuỗi làm đối số mẫu

Tôi đã thử rất nhiều giải pháp khả thi nhưng tất cả đều làm tôi thất vọng. Tôi sắp loại bỏ, nhưng trước tiên tôi muốn biết lý do tại sao một vài trong số họ không thành công.

Dưới đây là:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 

    char const *operator()() const { 
     return m_sz; 
    } 
}; 

template<class _rstr> 
string const Get() { 
    return _rstr(); 
} 

int main() { 
    cout << Get<String("hello")>() << endl; 
    return 0; 
} 

Và:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 
}; 

template<String const &_rstr> 
string const Get() { 
    return _rstr.m_sz; 
} 

int main() { 
    String constexpr str = "hello"; 
    cout << Get<str>() << endl; 
    return 0; 
} 

Mục đích là để tìm một cách thoải mái để vượt qua một chuỗi chữ đến chức năng Nhận vô dụng, mà trả về mẫu đối số của nó như là một std :: string object.

EDIT: xin lỗi, có thể câu hỏi chính của tôi không rõ ràng. Câu hỏi của tôi là: tại sao hai đoạn đó lại thất bại?

+1

Trong trường hợp đầu tiên, 'String ("hello")' là một giá trị, không phải là một kiểu, vì vậy nó không thể được chuyển tới khuôn mẫu 'Get <>' mà mong đợi một kiểu. Trong trường hợp thứ hai, C++ 11 không cho phép các kiểu tùy ý do người dùng định nghĩa (như 'String') là các tham số mẫu. –

Trả lời

3

lại: OP của bạn: I'd like to know why a couple of them failed.

Các bình luận bằng @NatanReed là đúng:

  • đoạn đầu tiên của bạn không thành công vì Get cần một TYPE và được đưa ra một object.
  • Đoạn mã thứ hai của bạn không thành công do xác định đối số mẫu là tham chiếu đến đối tượng bất hợp pháp.
    • cho đến khi C++ 2003, tức là. Sau đó, reference to an object trở thành hợp pháp.

Đối số mẫu phải là hằng số từ một nhóm loại giới hạn.

  • Xem: ISO/IEC 14.882-2.003 §14.1: thông số Template
  • Xem: ISO/IEC 14.882-2.003 §14.3.2: đối số mẫu không loại

Và thậm chí sau đó, String constexpr str = "hello"; phải có liên kết bên ngoài. Vì vậy, đặt nó trên ngăn xếp bên trong của main() sẽ không hoạt động.

này cung cấp cho một thử:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 
}; 

template<String const &_rstr> 
string const Get() { 
    return _rstr.m_sz; 
} 

extern String constexpr globally_visible_str = "hello"; 
int main() { 
    cout << Get<globally_visible_str>() << endl; 
    return 0; 
} 
13

Bạn không thể sử dụng chuỗi ký tự làm đối số mẫu, vì lý do đơn giản là không thể chỉ định hai trường hợp của chữ với cùng một văn bản có cùng một đối tượng hay không. Trong Nói cách khác, đưa ra:

template <char const* str> 
class TC {}; 

TC< "xyz" > v1; 
TC< "xyz" > v2; 

Nó sẽ là không xác định liệu v1v2 có cùng loại hay không.

Bạn có thể sử dụng char const[] biến như các đối số mẫu, Tuy nhiên, kể từ khi họ có một địa chỉ được xác định:

template <char const* str> 
class TC {}; 

extern char const xyz[] = "xyz"; 
TC<xyz> v1; 
TC<xyz> v2; 

Trong trường hợp này, v1v2 được đảm bảo để có cùng loại.

EDIT:

Tôi nghĩ rằng C++ 11 loại bỏ sự cần thiết của các extern vào định nghĩa của chuỗi, ít nhất nếu chuỗi và instantiation là tất cả trong các đơn vị dịch tương tự. Tuy nhiên, tôi không phải là ; một lần tôi đã làm một cái gì đó như thế này, tôi không có quyền truy cập vào C++ 11.

+0

Tôi có thể xác nhận giới hạn của 'extern' đã bị xóa vì tôi đã thử mà không có nó và nó hoạt động (đó là một trong nhiều lần thử của tôi). Ngoài ra, tôi biết tôi không thể chuyển trực tiếp chuỗi ký tự chuỗi nhưng tôi đã cố gắng phá vỡ điều này theo một cách nào đó và có thể không thành công. –

+0

Làm rõ: tuyên bố biến có liên kết bên ngoài (thường là một biến toàn cầu) không phải là "thoải mái" đối với tôi.Có thể sử dụng một số loại bộ điều hợp 'Chuỗi', như trong hai đoạn của tôi, thoải mái hơn nhiều. –

5

Bạn có thể "mô phỏng" chuỗi với C++ 11 mẫu variadic:

in này:

hello world !!!

+0

Có, tôi đã thử nó và nó hoạt động nhưng nó không phải là thoải mái hoặc. Dù sao thì, câu hỏi chính của tôi là: tại sao hai đoạn trích mà tôi đăng không hoạt động? –

5

Tôi biết bài này là cũ nhưng tôi đã không tìm thấy bất kỳ giải pháp cho vấn đề này ở đây, và có lẽ ai đó sẽ quan tâm đến cách giải quyết của tôi:

template <int N> 
constexpr int string_literal_length(const char (&str)[N]) { 
    return N - 1; 
} 

template <int PassedLength, int CountedLength, char... Characters> 
struct string_literal { 
    static_assert(PassedLength == CountedLength, "Passed to STRING_LITERAL length does not match the length of string..."); 
}; 

#define STRING_LITERAL(N, str) string_literal<N, string_literal_length(str), STRING_LITERAL_##N(str)> 

// ... as long as we need it ... 
#define STRING_LITERAL_128(str) STRING_LITERAL_127(str), str[127] 
#define STRING_LITERAL_127(str) STRING_LITERAL_126(str), str[126] 
#define STRING_LITERAL_126(str) STRING_LITERAL_125(str), str[125] 
#define STRING_LITERAL_125(str) STRING_LITERAL_124(str), str[124] 
// ... 
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), str[4] 
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), str[3] 
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), str[2] 
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), str[1] 
#define STRING_LITERAL_1(str) str[0] 

Bây giờ sử dụng:

template <class SLiteral> 
struct usage_of_string_literal { 
}; 

int main() { 
    usage_of_string_literal<STRING_LITERAL(12, "123456789012")> uosl; 
} 

Đáng tiếc là một phải cung cấp độ dài của chuỗi để làm cho nó làm việc nhưng nó' vẫn là giải pháp thoải mái hơn variadic đồng bằng mẫu arg của chars, và chiều dài được xác minh bởi các static_assert quá trình biên dịch có thể giúp để chọn giá trị thích hợp ...


Sửa

Thêm một mẫu ma thuật. Đây là một trong cách sử dụng ngắn mạch để thoát khỏi kích thước chuỗi từ khai STRING_LITERAL (C++ 17):

#include <type_traits> 
#include <utility> 

#define MAX_STRING_LITERAL_LENGTH 11 
#define STRING_LITERAL(str) string_literal<char_pack<STRING_LITERAL_11(str)>>::s 

#define STRING_LITERAL_11(str) STRING_LITERAL_10(str), ((TERMINATED_10(str))?(str[10]):('\0')) 
#define STRING_LITERAL_10(str) STRING_LITERAL_9(str), ((TERMINATED_9(str))?(str[9]):('\0')) 
#define STRING_LITERAL_9(str) STRING_LITERAL_8(str), ((TERMINATED_8(str))?(str[8]):('\0')) 
#define STRING_LITERAL_8(str) STRING_LITERAL_7(str), ((TERMINATED_7(str))?(str[7]):('\0')) 
#define STRING_LITERAL_7(str) STRING_LITERAL_6(str), ((TERMINATED_6(str))?(str[6]):('\0')) 
#define STRING_LITERAL_6(str) STRING_LITERAL_5(str), ((TERMINATED_5(str))?(str[5]):('\0')) 
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), ((TERMINATED_4(str))?(str[4]):('\0')) 
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), ((TERMINATED_3(str))?(str[3]):('\0')) 
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), ((TERMINATED_2(str))?(str[2]):('\0')) 
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), ((TERMINATED_1(str))?(str[1]):('\0')) 
#define STRING_LITERAL_1(str) str[0] 


#define TERMINATED_10(str) TERMINATED_9(str) && str[9] 
#define TERMINATED_9(str) TERMINATED_8(str) && str[8] 
#define TERMINATED_8(str) TERMINATED_7(str) && str[7] 
#define TERMINATED_7(str) TERMINATED_6(str) && str[6] 
#define TERMINATED_6(str) TERMINATED_5(str) && str[5] 
#define TERMINATED_5(str) TERMINATED_4(str) && str[4] 
#define TERMINATED_4(str) TERMINATED_3(str) && str[3] 
#define TERMINATED_3(str) TERMINATED_2(str) && str[2] 
#define TERMINATED_2(str) TERMINATED_1(str) && str[1] 
#define TERMINATED_1(str) str[0] 

template <char... Cs> 
struct char_pack { 
    static constexpr char const arr[sizeof...(Cs) + 1] = {Cs..., 0}; 
    static constexpr std::size_t non_zero_count = (((Cs != 0)?1:0) + ...); 
    static_assert(non_zero_count < MAX_STRING_LITERAL_LENGTH, "You need to create more macros"); 
}; 

template <char... Cs> 
constexpr char const char_pack<Cs...>::arr[sizeof...(Cs) + 1]; 

template <char... Cs> 
constexpr std::size_t char_pack<Cs...>::non_zero_count; 

template <class CP, class = void, class = std::make_index_sequence<CP::non_zero_count>> 
struct string_literal; 

template <char... Cs, std::size_t... Is> 
struct string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>> { 
    static constexpr char const s[sizeof...(Cs) + 1] = {Cs..., '\0'}; 
}; 

template <char... Cs, std::size_t... Is> 
constexpr char const string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>>::s[sizeof...(Cs) + 1]; 

template <char... Cs, std::size_t... Is> 
struct string_literal<char_pack<Cs...>, std::enable_if_t<!(Cs && ...)>, std::index_sequence<Is...>>: string_literal<char_pack<char_pack<Cs...>::arr[Is]...>> { }; 

template <const char *> 
struct foo {}; 

int main() { 
    foo<STRING_LITERAL("abcdefghij")> f; 
    static_cast<void>(f); 
} 

[live demo]

+0

Tôi nghĩ rằng việc vượt qua chiều dài có thể được loại bỏ một cách trivially bằng cách có thêm một macro vô hướng khác .. '#define STRING_LITERAL_ (STR) STRING_LITERAL (sizeof (STR) - 1, STR)'. Cách sử dụng: 'STRING_LITERAL _ (" 123456789012 ")'. BTW, có một cơ sở tăng cường là tốt, như đã đề cập trong [câu trả lời này] (http://stackoverflow.com/a/18154638/514235). Tuy nhiên, câu trả lời của bạn tốt là nó cung cấp giải pháp readymade. Bạn cũng có thể đặt một mảng 'const char'" bên trong 'class string_literal', nó sẽ đơn giản hồi tưởng lại tất cả các ký tự riêng lẻ thành chuỗi. – iammilind

+1

Xác minh mã của bạn và có vẻ như bất kỳ điều gì bạn đã làm đều chính xác; tức là chúng ta cần phải vượt qua độ dài một cách rõ ràng dưới dạng một chữ số để mã này hoạt động. Tôi đã được ấn tượng rằng, bạn đã thực hiện cách 'MPLLIBS_STRING' của boost được thực hiện. Dường như phải có một số thủ đoạn phức tạp để macro đó hoạt động. – iammilind

+0

@iammilind Tôi nghe nói về 'BOOST_HANA_STRING' nhưng theo như tôi biết nó không thể được sử dụng trong bối cảnh không được đánh giá khi nó sử dụng lambdas ... Tôi sẽ tra cứu' MPLLIBS_STRING'. Đối với chiều dài nó không thể được trao đổi với 'sizeof' bởi vì tôi đã sử dụng nối để làm vòng lặp mở rộng đơn giản có thể họ đã sử dụng một số cơ chế lặp tiền xử lý phức tạp hơn ở đây ... –

Các vấn đề liên quan