2013-08-03 87 views
7

我试图找到一个舒适的方式将字符串文字作为模板参数传递。我并不关心支持尽可能多的编译器,我使用最新版本的g ++与--std=c++0x尝试将字符串文字作为模板参数传递

我尝试了很多可能的解决方案,但都让我失望。我有点放弃,但首先我想知道为什么其中几个失败。

在这里,他们是:

#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; 
} 

和:

#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; 
} 

的目标是找到一个舒适的方式来传递一个字串无用获取函数,该函数返回其模板参数作为std :: string对象。

编辑:对不起,也许我的主要问题不清楚。我的问题是:为什么这两个片段失败?

+1

在第一种情况下,'字符串(“你好”)'是一个值,而不是一个类型,所以它不能传递给模板'Get <>',它需要一个类型。在第二种情况下,C++ 11不允许任意用户定义的类型(比如'String')作为模板参数。 –

回答

3

回复:你OP:I'd like to know why a couple of them failed.

通过@NatanReed的评论是正确的:因为Get需要TYPE并且给出object

  • 你的第一个片段失败。
  • 您的第二个片段失败,因为将模板参数定义为对对象的引用是非法的。
    • 直到C++ 2003,也就是说。然后reference to an object成为合法。

模板参数必须是从一组有限的类型的常量。

  • 见:ISO/IEC 14882-2003§14.1:模板参数
  • 见:ISO/IEC 14882-2003§14.3.2:模板非类型参数

即使这样, String constexpr str = "hello";必须有外部链接。所以把它放在main()里面的堆栈里是不行的。

这给一试:

#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

您不能使用字符串文字作为模板参数,对于 这个简单的原因,它没有指定具有相同文本的 文字的两个实例是否是相同的对象。在其他 也就是说,给出:

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

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

这将是不确定v1v2是否有同类型 与否。

您可以使用char const[]变量作为模板参数, 然而,因为他们有一个定义的地址:

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

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

在这种情况下,v1v2保证具有相同 类型。

编辑:

我认为C++ 11消除了对字符串的 定义需要进行extern,至少如果字符串和 实例都在相同的翻译单元。然而,我不是 ;有一次我做了这样的事情,我没有 有权访问C++ 11。

+0

我可以证实'extern'的限制已经被删除,因为我已经尝试了它,并且它可以工作(这是我多次尝试之一)。 另外,我知道我不能直接传递字符串文字,但我试图以某种方式规避这种情况,并可能失败。 –

+0

澄清:通过外部链接(通常是全局链接)声明一个变量对我来说并不“舒服”。能够使用某种'String'适配器,就像我的两个片段一样,它更加舒适。 –

5

可以 “模拟” 与C++ 11个可变参数模板字符串:

此打印:

世界你好!

+0

是的,我已经尝试过它,但它也不舒服。无论如何,我的主要问题是:为什么我发布的两个代码片段不工作? –

5

我知道后是旧的,但我还没有找到这个问题的任何解决方案在这里,也许有人会感兴趣的我的解决方法:

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] 

现在用法:

template <class SLiteral> 
struct usage_of_string_literal { 
}; 

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

不幸的是一个必须提供的字符串的长度得到它的工作,但它”仍然更舒适的解决方案比字符的普通可变参数ARG模板,长度由static_assert验证这样编译器可以帮助挑选合适的值...


编辑

还有一个模板魔法。这一个是利用短路摆脱串大小的从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

我认为长度的传递可以通过又一个间接宏来消除。'#define STRING_LITERAL_(STR)STRING_LITERAL(sizeof(STR)-1,STR)'。用法:'STRING_LITERAL _(“123456789012”)'。顺便说一句,还有一个助推设施,如[本答案](http://stackoverflow.com/a/18154638/514235)中所述。但是,您的答案很好,它提供了现成的解决方案。你也可以在'class string_literal'里面放置一个'const char''数组',它将把所有单独的字符重新记录到字符串中。 – iammilind

+1

已验证您的代码,看来您所做的任何事情都是正确的;即我们需要将该长度明确地作为文字数字传递,以便此代码正常工作。我的印象是,你已经实现了boost的'MPLLIBS_STRING'的实现方式。看来这个宏必须有一些复杂的技巧才能工作。 – iammilind

+0

@iammilind我听说过'BOOST_HANA_STRING',但据我所知它不能用于未评估的上下文中,因为它使用lambda表达式...我会查找'MPLLIBS_STRING'。至于长度,它不能与'sizeof'交换,因为我使用连接来做简单的可扩展循环,也许他们在这里使用了一些更复杂的预处理器循环机制...... –

相关问题