在编译时在模板中连接编译时字符串?

5

目前我有:

template <typename T> struct typename_struct<T*> {
    static char const* name() { 
        return (std::string(typename_struct<T>::name()) + "*").c_str(); 
    }
};

我想知道是否有方法可以避免在执行连接操作时必须分配字符串的情况。这一切都发生在编译时,即当我引用typename_struct::name()时,我希望得到字符串"int****"。(请假设我已经声明了一个相应的特化版本来返回"int")。
现在的代码是否只在编译时使用std::string进行连接?(我可以接受这种情况)还是这样的调用会导致4个基于std::string的连接在运行时执行?(我不能接受这种情况)

6
避免返回无效指针(即已释放的内存)会更好。 - Mike Seymour
@MikeSeymour 很好的观点。将返回类型设置为char const*似乎并不实际。 - Steven Lu
std::string 使用动态内存,因此我想不到在编译时这样做的任何理由,尤其是为了性能。 - Neil Kirk
@NeilKirk 的意思是我不需要使用 std::string - Steven Lu
拥有一个真正的编译时C++字符串类型将是一个很好的标准工具,而且这是可行的,但目前还没有实现。不过,最近的这个提案很有意思。你可以在这里找到更多相关信息。 - scry
显示剩余2条评论
4个回答

5

不使用递归模板的另一种方法(但需要 C++14):

#include <utility>
template<int...I> using is      = std::integer_sequence<int,I...>;
template<int N>   using make_is = std::make_integer_sequence<int,N>;

constexpr auto size(const char*s) { int i = 0; while(*s!=0){++i;++s;} return i; }

template<const char*, typename, const char*, typename>
struct concat_impl;

template<const char* S1, int... I1, const char* S2, int... I2>
struct concat_impl<S1, is<I1...>, S2, is<I2...>> {
    static constexpr const char value[]
    {
        S1[I1]..., S2[I2]..., 0
    };
};

template<const char* S1, const char* S2>
constexpr auto concat {
    concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value
};

例子:

constexpr const char a[] = "int";
constexpr const char c[] = "**";

#include <iostream>
int main()
{
    std::cout << concat<a,b> << '\n';
}

将字符添加到字符串中也可以像这样实现,通过将第二个const char*参数替换为char...


如果我尝试constexpr auto a = "123"; constexpr auto b = "456"; constexpr auto c = concat<a, b>;我会得到一个错误。有任何线索为什么? - BioAbner J

5
您可以使用类似以下的方式。所有操作都在编译时完成。通过特化base_typename_struct来定义您的原始类型。
注:原始类型不是指C++中的int、float等基础类型,而是指用户自己定义的最基本的数据类型。
template <const char* str, int len, char... suffix>
struct append {
  static constexpr const char* value() {
    return append<str, len-1, str[len-1], suffix...>::value();
  }
};

template <const char* str, char... suffix>
struct append<str, 0, suffix...> {
  static const char value_str[];
  static constexpr const char* value() {
    return value_str;
  }
};

template <const char* str, char... suffix>
const char append<str, 0, suffix...>::value_str[] = { suffix..., 0 };


template <typename T>
struct base_typename_struct;

template <>
struct base_typename_struct<int> {
  static constexpr const char name[] = "int";    
};


template <typename T, char... suffix>
struct typename_struct {
  typedef base_typename_struct<T> base;
  static const char* name() {
    return append<base::name, sizeof(base::name)-1, suffix...>::value();
  }
};

template <typename T, char... suffix>
struct typename_struct<T*, suffix...>:
  public typename_struct<T, '*', suffix...> {
};


int main() {
  cout << typename_struct<int****>::name() << endl;
}

我认为可以使用宏自动将 int 转换为 'i','n','t'。谢谢。 - Steven Lu
@StevenLu,你可以使用宏来实现:EXPAND("int") => 'i', 'n', 't'。使用 BOOST_PP_REPEAT 和某种形式的 PushBack<str, c> 即可完成。详见 - chris
不知道上面的代码是否标准 - Visual Studio 2015会报错:error C4576: a parenthesized type followed by an initializer list is a non-standard explicit type conversion syntax,并且拒绝编译。 - GaspardP
啊!你可以通过在Visual Studio 2015中使用静态static const char value_str[];并初始化为{ suffix..., 0 },然后在value()中返回value_str来使其工作。 - GaspardP
是的,那是GCC的扩展。我编辑了代码。我很惊讶在一个关于C++晦涩知识的旧回答上收到了评论 :) - pdw
显示剩余5条评论

0
#include <iostream>

//
***************************************************************************
template<const char* S1, const char* S2, size_t I1 = 0, size_t I2 = 0, char = S1[I1], char = S2[I2], char... Chars>
struct Concat : Concat<S1, S2, I1 + 1, I2, S1[I1 + 1], S2[I2], Chars..., S1[I1]>
{
};

// ****************************************************************************
template<const char* S1, const char* S2, size_t I1, size_t I2, char C2, char... Chars>
struct Concat<S1, S2, I1, I2, 0, C2, Chars...> : Concat<S1, S2, I1, I2 + 1, 0, S2[I2 + 1], Chars..., S2[I2]>
{
};

// ****************************************************************************
template<const char* S1, const char* S2, size_t N1, size_t N2, char... Chars>
struct Concat<S1, S2, N1, N2, 0, 0, Chars...>
{
  static constexpr const char Text[] = { Chars... , 0 };
};

// ****************************************************************************
static constexpr const char A[] = "123";
static constexpr const char B[] = "456";

// ****************************************************************************
int main(int argc, char* argv[]){
  std::cout << Concat<A, B>::Text << std::endl;
  return 0;
}

0

我不确定你在寻找什么,但我相信你对typeid和名称重整有兴趣(你使用哪个编译器?)

在gcc中,应该是这样的:

#include<iostream>
#include <string>
#include <typeinfo>
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
using namespace std;

std::string demangle(const char* name) {
    int status = -4; // some arbitrary value to eliminate the compiler warning

    // enable c++11 by passing the flag -std=c++11 to g++
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };
    return (status==0) ? res.get() : name ;
}

template <typename T> struct typename_struct {
  static std::string name() {
    std::string typeName = typeid(T).name();
    return demangle(typeName.c_str());
  }
};

int main(){

  cout << typename_struct<int****>::name(); // Prints "int****"

  return 0;
}

http://ideone.com/nLsFF0

来源:https://dev59.com/fnVC5IYBdhLWcg3weBA-#4541470

关于你的问题:那些不是constexpr结构,因此尽管模板参数和代码在编译时实例化,但评估仍然发生在运行时。

使用模板并不意味着其中包含的每个指令都将在“编译时”执行和解决。

我认为你无法在编译时完成整个过程,因为涉及到解缠函数(ABI特定)。如果我误解了你的问题,请告诉我。


我知道typeid和name-demangling,我正在并行进行(这样我就可以检查它--它通常会使模板实例化的类型变得丑陋)。我的项目通过不同的方式实现“反射”,因此我正在尝试看看是否可以定义模板来涵盖所有基础。到目前为止,它一直运作良好。现在你说它们不是constexpr结构,是因为我使用std::string进行连接吗?如果我可以使所有这些模板也成为constexpr,那么有没有办法强制在编译时发生连接(即使这根本没有任何意义)? - Steven Lu
如果每个“指针链”在运行时引发线性时间的字符串连接操作,其实并不是什么大问题。主要是因为我只需要这个来保持“兼容性”。在几乎所有情况下,使用这些疯狂的类型都是不明智的。 - Steven Lu
@StevenLu constexpr函数有几个限制条件,请看这里:http://en.cppreference.com/w/cpp/language/constexpr。这对于`std::string`不起作用,您可能还想在这里查看字符串连接:https://dev59.com/61_Va4cB1Zd3GeqPXM0j#9054709。 - Marco A.
@StevenLu:如果没有编译器的支持,你无法以有用的方式执行反射。 - Puppy
@ Marco 很棒!!!非常感谢提供的链接。 @Puppy 我还没完成,但是等我能给你展示证据的时候,我会的。C++11有很多好东西。这可能已经足够了。(提示:大量的预处理器宏和模板) - Steven Lu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接