コンパイル時に文字列を結合する

コンパイル時に使える文字列型のようなものを用意します。
2つの文字列を受け取る際に、templateでその文字列長を推論して得られるようにしておき、あとはindex_tuple idiomを用いて結合処理をして新しい文字列型を返します。
このようにすれば、副作用を用いずに2つの文字列を結合することが出来ます。
どうすればうまくいくか判ったところで、実際にコンパイル時文字列を使用したい場合はsprout::stringを使えば良いと思います。

#include <type_traits>
#include <sprout/index_tuple.hpp>

template<class T, std::size_t N>
struct c_str
{
public:
    T value[N ? N : 1];
    
    template<class T1, std::size_t N1>
    constexpr bool operator==(T1(&str)[N1]) const
    {
        return N == N1 && cmp_impl(str);
    }
    
private:
    template<class T1, std::size_t N1>
    constexpr bool cmp_impl(T1(&str)[N1], std::size_t step = 0, bool result = true) const
    {
        return step < N1
        ? cmp_impl(str, step + 1, result && value[step] == str[step])
        : result
        ;
    }
};

template<class T, std::size_t size1, std::size_t size2, sprout::index_t... indexes>
constexpr auto cat_impl(const T (&str1)[size1], const T (&str2)[size2], sprout::index_tuple<indexes...>)
-> decltype(
            c_str<T, size1 + size2 - 1>{{(indexes < (size1 - 1) ? str1[indexes] : str2[indexes - (size1 - 1)])...}}
            )
{
    return c_str<T, size1 + size2 - 1>{{(indexes < (size1 - 1) ? str1[indexes] : str2[indexes - (size1 - 1)])...}};
}

template<class T, std::size_t size1, std::size_t size2>
constexpr auto cat(const T (&str1)[size1], const T (&str2)[size2])
-> decltype(
            cat_impl(str1, str2, typename sprout::index_range<0, size1 + size2 - 1>::type())
            )
{
    return cat_impl(str1, str2, typename sprout::index_range<0, size1 + size2 - 1>::type());
}

int main()
{
    constexpr auto str1 = cat("Hello, ", "World!");
    static_assert(str1 == "Hello, World!", "");
}