【问题标题】:Passing const char* as template argument将 const char* 作为模板参数传递
【发布时间】:2011-04-15 23:03:56
【问题描述】:

为什么你不能在这里传递文字字符串?我用一个非常轻微的解决方法让它工作。

template<const char* ptr> struct lols {
    lols() : i(ptr) {}
    std::string i;
};
class file {
public:
    static const char arg[];
};
decltype(file::arg) file::arg = __FILE__;
// Getting the right type declaration for this was irritating, so I C++0xed it.

int main() {
    // lols<__FILE__> hi; 
    // Error: A template argument may not reference a non-external entity
    lols<file::arg> hi; // Perfectly legal
    std::cout << hi.i;
    std::cin.ignore();
    std::cin.get();
}

【问题讨论】:

  • const char file::arg[] = __FILE__; 这应该可以让你回到 C++03 :)。
  • @Kenny:我试过了。编译器抛出错误。
  • @Dead:奇怪。 g++ 编译良好(使用 -pedantic)。
  • @Kenny:烦人,我一直认为这是不可能的。现在我有了它,我似乎无法让它做任何有趣或有用的事情。
  • 请注意,如果在命名空间范围内,您将需要extern const char arg[] = __FILE__; 为数组提供外部链接(默认情况下,const 对象具有内部链接)。

标签: c++ string templates


【解决方案1】:

因为这不是一个有用的实用程序。由于它们不是模板参数的允许形式,因此它目前不起作用。

让我们假设它们有效。因为它们不需要为使用的相同值具有相同的地址,所以即使您的代码中具有相同的字符串文字值,您也会得到不同的实例化。

lols<"A"> n;

// might fail because a different object address is passed as argument!
lols<"A"> n1 = n;

您可以为您的文本编辑器编写一个插件,用逗号分隔的字符文字列表替换字符串并返回。使用可变参数模板,您可以以某种方式“解决”这个问题。

【讨论】:

  • 只是问他们为什么不给他们相同的地址。
  • @DeadMG 因为字符串文字“A”在某些情况下可能是立即数并且根本没有地址?
  • @SlashV 我知道这已经有一段时间了,但你能介意扩展一下吗?我对解释也有点兴趣。
  • @Zadirion ..还有一会儿。鉴于模板参数需要获取其地址,编译器需要为字符串文字“A”提供唯一地址。编译器必须这样做是不必要的限制。文字“A”可能是内存中某处的常量(并且有一个地址),但编译器可能有多个副本,因此地址不会是唯一的,或者文字“A”可能是机器代码的“立即”操作数指令bit.ly/17HySsu,因此不在任何地方的数据存储器中,因此根本没有地址。
  • @SlashV:这是完全错误的。该语言已经要求编译器在许多上下文中合并全局变量(例如内联函数)。强迫他们汇集字符串文字不会或多或少费力。至于立即数,字符串文字必须具有程序持续时间存储,因此将字符串文字的内容存储在立即数中已经是非法的。
【解决方案2】:

这是可能的,但模板参数必须具有外部链接,这排除了使用文字字符串并降低了这样做的实用性。

我的一个例子是:

template<const char* name, const char* def_value=empty_>
struct env : public std::string
{
    env()
    {
        const char* p = std::getenv(name);
        assign(p ? p : def_value);
    }
};

extern const char empty_[] = "";

std::string test = env<empty_>();

【讨论】:

  • ...除了你不应该继承namespace std中的任何东西
【解决方案3】:

这就是我的做法。对我来说更有意义:

struct MyString { static const std::string val; };
const std::string MyString::val = "this is your string";

template<typename T>
void func()
{
  std::cout << T::val << std::endl;
}

void main()
{
  func<MyString>();
}

【讨论】:

    【解决方案4】:

    这适用于类,并且在 IMO 中很有用。实现快速而肮脏,但很容易变得更干净:

    #include <stdio.h>
    #include <string.h>
    
    struct TextTag { const char *text; };
    
    template <const TextTag &TRUE, const TextTag &FALSE>
    struct TextTaggedBool
    {
      const char *GetAsText() const { return m_value ? TRUE.text: FALSE.text; }
      void SetByText(const char *s) { m_value = !strcmp(s, TRUE.text); }
      bool m_value;
    };
    
    class Foo
    {
    public:
        void method()
        {
            m_tbool.SetByText("True!");  printf("%s\n", m_tbool.GetAsText());
            m_tbool.SetByText("False!"); printf("%s\n", m_tbool.GetAsText());
            m_tbool.m_value = true;  printf("%s\n", m_tbool.GetAsText());
            m_tbool.m_value = false; printf("%s\n", m_tbool.GetAsText());
        }
    
    private:
        static constexpr TextTag TrueTag = { "True!" };
        static constexpr TextTag FalseTag = { "False!" };
        TextTaggedBool<TrueTag, FalseTag> m_tbool;
    };
    
    void main() { Foo().method(); }
    

    输出:

    没错! 错误的! 真的! 假的!

    【讨论】:

      【解决方案5】:

      好问题,我想我会把帽子扔进戒指...我想您可以将指向静态变量的指针作为非类型模板参数传递。从 C++20 开始,它看起来不会成为问题......在此之前,这里有一些便宜的宏可以让它工作。

      template <const char *Name, typename T>
      struct TaggedValue {
        static constexpr char const *name{Name};
        T value;
        friend ostream &operator<<(ostream &o, const TaggedValue &a) {
          return o << a.name << " = " << a.value;
        }
      };
      
      #define ST(name, type)\
        const char ST_name_##name[]{#name};\
        using name = TaggedValue<ST_name_##name,type>;
      
      ST(Foo, int);
      ST(Bar, int);
      ST(Bax, string);
      
      int main() {
        cout << Foo{3} << endl;
        cout << Bar{5} << endl;
        cout << Bax{"somthing"} << endl;
      }
      

      C++20 注释(编辑)

      我最近很少使用 c++,所以如果这不是 100% 正确,我很抱歉。有一条关于为什么这在 c++20 中不会成为问题的评论。根据template_parameters上的参考:

      非类型模板参数必须具有结构类型,它是以下类型之一(可选 cv 限定,限定符被忽略):

      ...

      • 浮点类型;
      • 具有以下属性的文字类类型:
        • 所有基类和非静态数据成员都是公共的、不可变的,并且
        • 所有基类和非静态数据成员的类型都是结构类型或其(可能是多维的)数组。

      这让我相信以下代码会起作用:

      struct conststr
      {
          const char * const p;
          template<std::size_t N>
          constexpr conststr(const char(&a)[N]) : p(a)/*, sz(N - 1) */{}
      };
      
      template<conststr s>
      struct A{};
      
      int main(int argc, char **argv) {
          A<conststr("foo")> x;
      }
      

      (同样,我不能 100% 确定这是否 100% 正确)。但它没有,至少在我的机器上没有g++ -std=c++2a (g++ --version == g++ (Debian 8.3.0-6) 8.3.0)。它也不适用于double。 This guy 在这里提供了更详细的历史记录,可能还有更好的参考资料,我可能完全不正确。

      【讨论】:

      • 为什么它不会成为 C++20 的问题?
      • 我已经编辑了我的答案来解释那个晦涩的评论。我希望它有用...
      • 谢谢。我应该补充一点,我已经能够将字符串传递给模板参数(而不是字符串文字本身),而且我不确定我使用的代码是否依赖 C++20 来使其工作。它似乎适用于 C++03 到 C++20 的所有版本。 (代码:godbolt.org/z/jr5K5nvz8)
      猜你喜欢
      • 1970-01-01
      • 2020-04-15
      • 1970-01-01
      • 1970-01-01
      • 2018-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-08
      相关资源
      最近更新 更多