English 中文(简体)
对各类非类型模板参数进行总结
原标题:Abstracting over types of non-type template parameters

我想写一个模板,可以解构一种类型,将其解构成一个带有非类型模板参数的模板,并加上其非类型模板参数。 例如,它将解构 Array<5> in template<int> Array and ,但对于任何类型的非类型模板参数(直观类型、指针、成员指针等)都将通用。

第一次尝试, 使用模板专业化 :

template<typename T> struct foo { enum { n = 1 }; };

template<int x> struct bar { enum { n = x }; };

template<typename T, template<T> class X, T x>
struct foo< X<x> > { enum { n = x }; }; // here x must be of integral type, but that s just for testing

int main(int, char**) { return foo< bar<16> >::n; }

Clan 3.1 说:

test145.cpp:6:8: warning: class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used
struct foo< X<x> > { enum { n = x }; };
       ^~~~~~~~~~~
test145.cpp:5:19: note: non-deducible template parameter  T                      
template<typename T, template<T> class X, T x>
                  ^
1 warning generated.

第二次尝试, 使用函数模板 :

template<typename T, T x> 
struct box 
{ 
    static constexpr T value() { return x; }
};

template<typename T, template<T> class X, T x>
box<T, x> foo(X<x>);

template<int> struct asdf { };

int main(int, char**) { return decltype(foo(*(asdf<9>*)0))::value(); }

Clang说:

test150.cpp:12:41: error: no matching function for call to  foo 
int main(int, char**) { return decltype(foo(*(asdf<9>*)0))::value(); }
                                        ^~~
test150.cpp:8:11: note: candidate template ignored: couldn t infer template argument  T 
box<T, x> foo(X<x>);
          ^
1 error generated.

海合会4.7说类似的话。

这是根本的限制吗?

bonus 问题 : 如果是的话, 那么是否有办法在一定数量的代码中处理所有无限的可能性, 即使它不那么简单和通用的代码? (例如, 指示器会变得困难 : 因为同样的原因, 您似乎无法写入 < code> template< T> I don't think you could write template< Tgt; 。 )

请不要问我为什么问。

最佳回答

答案可能来得较晚...

Missed Try...

(见 < strong> Correct 回答 & amp; < strong> C+/17 解决方案 下面)


This original answer is kept as a souvenir of my very first answer on SO.
Not exactly a fail, one would say. But rather, a first missed try... ;)
Now, jump to the next horizontal line...

当我遇到这个问题时,我正在寻找一个相关问题的答案。在阅读了这个问题之后,我告诉自己:'Humm...这就是我已经做过的事情。它有效。我到底怎么做到的?。然后,我继续寻找问题答案...

今天,我觉得我应该花一点时间提出解决这个问题的办法:

正如您已经注意到的,问题在于编纂者不知道如何推导 T 。 人们可以将错误信息解释为 。 请在 T 上给我一点帮助。

我工作的第一种版本是专门化 foo , 来源于类似于 std 的类别 ::integral_constant 。 Making >foo 来源于 std:::integral_constatant<T, x> 可能帮助编译者找出了 T 的类型。 (也许 MSVC -vs2019- 对我有点好)

总之,与此同时,我找到了更好的解决方案。 编译者不能不推断出 < code> T 的类型, 因为对于 < code > t 的类型, 不需要 < code > typename T 参数...


这是: <%% em> (C+/17 解决方案)

template<typename T> struct foo {};

template<auto x, template<decltype(x)> class X>
struct foo<X<x>> {
    using            arg_type   = decltype(x);
    static constexpr arg_type n = x;
};

//template<int x> struct bar { enum { n = x }; };
template<int x> struct bar;

using bar_16            = foo<bar<16>>;
using bar_16_arg_t      = typename bar_16::arg_type; // int
constexpr auto bar_16_n = bar_16::n;                 // 16

请注意,要使此功能有效,甚至不需要 < code>bar 成为完整的类型。 远端声明 < em> (如此示例中) < /em > 足以使 < em> 分解 < /em > 工作。

玩得开心点...


Correct Answer

o Notes

  • This answers the question asked 9 years ago, and,
  • The solution proposed here uses only C++11 features.
  • This solution manages only integral types.
    (other type is let as an exercise for the reader)

    If your compiler supports C++17 features, the solution posted above should be preferred, as it manages not only integral types.

Only interested in a working code example?
jump to: "Working Solutions"

o 序言

  • After my researches, it seems that a solution to this particular problem hasn t been found (or not published) until now. I thought I should go in a little more details about the "Whys" and "Hows". I hope it will be appreciated, but overall: useful...
  • I am currently in the process of writing a meta-programming library full of compile-time tools and features. I hope to be able to release it on GitHub anytime soon.
    (who knows) – Anyway...
  • What hasn t been my frustration when I realized that my first answer was correct only since C++17... – Not exactly "on time", one could say...:P
  • With all the machinery of "compile-time only" features I have in mind ATM, I was feeling like there should have had a means to do that 9 years ago.
  • I begun to think about how I would have had done that using only C++11 features and, after an hour or so, I found a working solution. (two in fact)
  • It took me a little more time to make it a usable solution. (two in fact)
  • And quite a bit more to right this post... :D

    After all, there might have compilers
    "just good enough" to understand C++11 only... :P

显然,由于当时可用的成套功能范围较小,
找到的解决方案是 just a little more verbose... :D

° 搜索过程

Firstly, one has to keep in mind that when a compiler outputs "cannot deduce"...
– It does not mean that there is an error (though there might have one).
– It rather means that the compiler is not as clever as one thinks it is.
– It means that one has to give a hand to the compiler for it to be able to do its job...

In clear?
– The compiler is kindly asking for you to do a part of its job.
– And there s good chances for you:

  • To end up doing most of the job yourself... :P

Here, the compiler says "cannot deduce the type of T".
Indeed, T is not used in the expression used as argument of the specialization of foo, and thus, it cannot be deduced from there...

首先必须做一些事情来表示 typename T x (类型为 T ) 之间的关联。当下想到的是,一个人需要类似于 std::integil_constant 的模板,该模板正是如此。它将该模板编码编码编码为 new type ,一个数值及其相应类型。

.................................................................

  • People susceptible to develop allergic reactions at sight of uppercase letters within identifier names should not continue reading this post!

Nothing new until there?
Perfect! Here it is:

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

下一步需要某些东西来创建 nonTypeParam 模板的示例,其中含有值及其相应类型...

  • It might be a template taking a type parameter.
  • This parameter would receive the type to decompose.
  • Then, one would have to specialize it somehow...

让我们试一试,从以下开始:

template<typename T> struct Extract { using Result = void; };

要完全抽象 Expresent 模板的专业化, 就必须写出类似的东西 :

template<typename T, T V, template<T> class C>
struct Extract<C<V>> { using Result = NonTypeParam<T, V>; };

导致同样的问题, 因为它与问题所使用的专业类型相同 。 在这一点上, 人们必须提醒汇编者不能做什么 。 它“ em> ” “ < strong> 不能推导 。 < code> T 的参数在我们的专业中应该用什么类型的别名...

In fact, the message is misleading somehow, because T is not even part of the expression passed as argument of the specialization. Thus, the problem is not to attribute a typename to the parameter T, it is rather to attribute a type to the parameter V...
Now, one should be able to ask the right questions:

  1. How can T be removed from the equation?
    • By explicitly defining the type of V.
  2. What are the possible types for the value of V?
    • The types allowed as non-type template parameters.

首先,首先,如何通过明确界定 V 的类型来看待专业化,例如, Charles 的类别?

template<char V, template<char> class C>
struct Extract<C<V>> { using Result = NonTypeParam<char, V>; };

,这有点烦人,但可能性是有限的。人们可能会找到一个方法来减少以后的申报。让我们添加另一个专业,一个受害者模板,然后测试一下...

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

template<typename T> struct Extract { using Result = void; };

template<char V, template<char> class C>
struct Extract<C<V>> { using Result = NonTypeParam<char, V>; };

template<std::size_t V, template<std::size_t> class C>
struct Extract<C<V>> { using Result = NonTypeParam<std::size_t, V>; };

template<std::size_t I> struct TestNonType1 {};

using Result          = typename Extract<TestNonType1<42>>::Result;
using RType           = typename Result::Type; // std::size_t
constexpr auto rValue = Result::Value;         // 42

No surprises, it works as expected...
... What are the possible types now?
According to the standard on template parameters:

非类型模板参数必须有一个结构型号,即以下类型之一 (可选择的 cv-合格,可忽略限定词) :

  • lvalue reference type (to object or to function);
  • an integral type;
  • a pointer type (to object or to function);
  • a pointer to member type (to member object or to member function);
  • an enumeration type;
  • std::nullptr_t; (since C++11)

For our case, the question asked for integral types.
Well, what does the standard says about integral types.
Let s have a look at std::is_integral to find out:

...如果 T 是类型 bool , >>, >char , > > , >>>>>> > , > >>>>>, >>>>>>code>code>code>load , , long ,或 any > any-定义的 intger 类型 ,包括 任何已签字的 , < unt > 和 < untrent > unign 和 < 和 > 和 > 和 < rough > sucv-d tract > plat > etctal

强者!强者!强者!

由于有9种类型 - if one acredit >>char88_t (仅来自C++20 ),并且认为执行定义的整数类型是这些整体类型 的大部分时间别名 -- -- 人们必须为以下目的进行专门化:

  • 9 signed.
  • 9 signed const.
  • 9 signed volatile.
  • 9 signed const volatile.
  • Which does 36 specializations.
  • And then, add 36 more for the unsigned versions?!

<强度 > 驱逐 [通知]

  • Without a doubt, this is the reason why no one (maybe really no one else) did that before...

Wait, wait, wait a minute...

应该再考虑一下这个问题,并再次提出正确的问题:

  • How is a non-type parameter read / interpreted ?
  • Does it make any sense for it to be volatile ?
  • If its value is part of the typename, isn t const implied somehow ?

"你一定找到了答案..."

– Likewise, there is no unsigned version of char16_t, char32_t and wchar_t.
– Moreover, if one reads a second time a little more carefully what the standard says about template parameters, one might see something that haven t had the attention it deserves...

一个非类型模板参数必须有一个结构类型, 结构类型是以下类型之一( 可选择 cv- 合格, < em% strong > 限定符被忽略 )

"他们好,好,好,好..."

– This will do a lot more job than one would have excepted at first... :P
– It turn out that, in the end, only 14 specializations of the Extract template are enough to manage 99% of all possible integral types...

...我认为这样一小撮代码 写太多的书了

请在下面找到解决方案 — — 请在这里为子孙( / em/ / strong) 提供解决方案, 希望它 < em> might < / em > 对某人( 至少对于第二个例子中使用的有趣的诡计) < / em > 有用 。

o 个人评论

我难以相信,这个9年前的问题没有找到早些时候的答案(以及认为我是唯一一个找到答案的“笨蛋”)


Working Solutions

Solution #1

这里没什么特别的 只是样板的常规专业...

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

namespace Details1 {

template<typename T> struct Extract { using Result = void; };

template<typename T, T V> using R = NonTypeParam<T, V>;

// boolean
template<bool V, template<bool> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
// signed types
template<char      V, template<char>      class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<char16_t  V, template<char16_t>  class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<char32_t  V, template<char32_t>  class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<wchar_t   V, template<wchar_t>   class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<short     V, template<short>     class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<int       V, template<int>       class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<long      V, template<long>      class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<long long V, template<long long> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
// unsigned types
template<unsigned char      V, template<unsigned char>      class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned short     V, template<unsigned short>     class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned int       V, template<unsigned int>       class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned long      V, template<unsigned long>      class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };
template<unsigned long long V, template<unsigned long long> class C> struct Extract<C<V>> { using Result = R<decltype(V), V>; };

} /* namespace Details1 */

template<typename T>
struct Extract1
{
    using Result = typename Details1::Extract<T>::Result;
};

// Victim template:
template<std::size_t I> struct TestNonType1 {};

// Usage:
using          Param  = typename Extract1<TestNonType1<42>>::Result;
using          PType  = typename Param::Type; // std::size_t
constexpr auto pValue = Param::Value;         // 42

Solution #2

在此解决方案中, 人们可以利用 < code> decltype 的力量来宣布功能模板超载, 功能模板超载在任何地方都无法定义...

template<typename T, T V>
struct NonTypeParam { using Type = T; static constexpr T Value = V; };

namespace Details2 {

template<typename T, T V> using R = NonTypeParam<T, V>;

// boolean
template<bool V, template<bool> class C> R<decltype(V), V> Extract(C<V> && _);
// signed types
template<char      V, template<char>      class C> R<decltype(V), V> Extract(C<V> && _);
template<char16_t  V, template<char16_t>  class C> R<decltype(V), V> Extract(C<V> && _);
template<char32_t  V, template<char32_t>  class C> R<decltype(V), V> Extract(C<V> && _);
template<wchar_t   V, template<wchar_t>   class C> R<decltype(V), V> Extract(C<V> && _);
template<short     V, template<short>     class C> R<decltype(V), V> Extract(C<V> && _);
template<int       V, template<int>       class C> R<decltype(V), V> Extract(C<V> && _);
template<long      V, template<long>      class C> R<decltype(V), V> Extract(C<V> && _);
template<long long V, template<long long> class C> R<decltype(V), V> Extract(C<V> && _);
// unsigned types
template<unsigned char      V, template<unsigned char>      class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned short     V, template<unsigned short>     class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned int       V, template<unsigned int>       class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned long      V, template<unsigned long>      class C> R<decltype(V), V> Extract(C<V> && _);
template<unsigned long long V, template<unsigned long long> class C> R<decltype(V), V> Extract(C<V> && _);

} /* namespace Details2 */

template<typename T>
struct Extract2
{
    using Result = decltype(Details2::Extract(std::declval<T>()));
};

// Victim template:
template<unsigned long long I> struct TestNonType2 {};

// Usage:
using          Param  = typename Extract2<TestNonType2<42>>::Result;
using          PType  = typename Param::Type; // std::size_t
constexpr auto pValue = Param::Value;         // 42

(7月25日,2021年7月25日)

  • Here below is an example of how a template declared with any type of non-type parameter can be decomposed.
  • Unfortunately, though this little piece of code seems to be using only C++11 language features, it cannot be compiled as C++11.
  • This code works perfectly, and does what it is meant to do, but is has to be compiled as C++17.
  • There definitely had a change in the standard since the addition of auto as a non-type template parameter which, I think (but couldn t find info on it), make the compilers interpret the pattern <typename T, template <T> class C, T V> as if it was a synonym of <auto V>.
/* Template allowing to separately retrieve the components
 * of a template having one non-type parameter.
 */
template<typename T, template <T> class C, T V>
struct TmplInfo;

/* Function to decompose a template having one non-type
 * parameter and return its corresponding TmplInfo type.
 */
template<typename T, template <T> class C, T V>
inline constexpr TmplInfo<T, C, V> ToTmplInfo(C<V> && o);

/* Our victim template...
 */
template<std::size_t I> struct Victim;

/* Aliases Victim<42> and then decompose it to a TmplInfo.
 */
using V42   = Victim<42>;
using VInfo = decltype(ToTmplInfo(std::declval<V42>()));

/* Compiled for x64 arch, this gives:
 * using VInfo = TmplInfo<std::size_t, Victim, 42Ui64>;
 */
问题回答




相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?