传递const char*作为模板参数

Pup*_*ppy 28 c++ string templates

为什么你不能在这里传递文字字符串?我使用了一个非常轻微的解决方法.

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();
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*itb 16

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

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

lols<"A"> n;

// might fail because a different object address is passed as argument!
lols<"A"> n1 = n;
Run Code Online (Sandbox Code Playgroud)

您可以为文本编辑器编写一个插件,用逗号分隔的字符文字列表替换字符串.使用可变参数模板,您可以通过某种方式以这种方式"解决"该问题.

  • 只是提出一个问题,为什么他们不只是给他们相同的地址. (7认同)
  • @Zadirion ..还有一次.鉴于模板参数需要采用其地址,编译器需要为字符串文字"A"提供唯一的地址.编译器必须这样做是不必要的限制.文字"A"在某处可以是内存中的常量(并且具有地址),但是编译器可能具有多个副本,因此地址不是唯一的,或者文字"A"可能是机器代码的"立即"操作数.指令http://bit.ly/17HySsu因此不在任何地方的数据存储器中,因此根本没有地址. (6认同)
  • @SlashV:这是完全错误的。该语言已经要求编译器在许多上下文中合并全局变量(例如内联函数)。强迫他们池字符串文字不会或多或少费力。至于立即数,字符串文字必须具有程序持续时间存储,因此将字符串文字的内容存储在立即数中已经是非法的。 (2认同)

Nat*_*nst 6

这是可能的,但模板参数必须具有外部链接,这排除了使用文字字符串并减轻了执行此操作的效用.

我的一个例子是:

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_>();
Run Code Online (Sandbox Code Playgroud)

  • ...除了你不应该从`namespace std`中的任何东西继承 (5认同)

Mic*_*ael 5

我就是这样做的.对我来说更有意义:

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>();
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*ell 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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

(再次强调,我不能 100% 确定这是否 100% 正确)。但事实并非如此,至少在我的机器上没有g++ -std=c++2ag++ --version == g++ (Debian 8.3.0-6) 8.3.0)。它也不起作用double这个人在这里对历史进行了更详细的描述,并且可能有更好的参考资料,而且我可能完全错误。

  • 谢谢。我应该补充一点,我已经能够将字符串传递到模板参数(而不是字符串文字本身),并且我不确定我使用的代码是否依赖于 C++20 来使其工作。它似乎适用于 C++03 到 C++20 的所有版本。(代码:https://godbolt.org/z/jr5K5nvz8) (2认同)