我真的很惊讶gcc和clang都接受这个代码:
#include <iostream>
#include <vector>
#include <type_traits>
template <class T, template <class, class = T> class TT, class Y>
T foo(TT<Y>) {
}
int main() {
static_assert(std::is_same<decltype(foo(std::vector<int>{})), std::allocator<int>>::value);
}
Run Code Online (Sandbox Code Playgroud)
gcc和clang是正确的,默认模板模板参数的值是推导上下文还是编译器扩展?
过了一会儿,我再次发现了模板模板参数的强大功能.请参阅以下代码段:
template <template <class> class TT, class T>
void foo(TT<T>) {
}
template <class T>
using typer = T;
int main() {
foo<typer>(int{});
}
Run Code Online (Sandbox Code Playgroud)
别名模板作为模板模板参数传递给模板,并进一步用于检测模板的其他参数,因为它是推导的上下文.美女!
然而,当需要推断别名模板本身时,看起来编译器会变得疯狂:
template <template <class> class>
struct tag{};
template <template <class> class TT, class T>
void foo(tag<TT>, TT<T>) {
}
template <class T>
using typer = T;
int main() {
foo(tag<typer>{}, int{});
}
Run Code Online (Sandbox Code Playgroud)
编译器当然是正确的,TT可以从两者tag<TT>以及TT<T> 参数中推断出来foo,并且int{}模板模板与类型参数模式不匹配.有没有办法保留演绎上下文T但是在TT非演绎语境中TT<T> …
这段代码:
template <template <typename> class T>
class A
{
};
template <typename T>
class B
{
A<B> x;
};
Run Code Online (Sandbox Code Playgroud)
不编译,我想自从A<B>被解释为A<B<T> >在B范围内.
那么,你如何B在其范围内作为模板模板参数传递?
如果我希望模板模板参数有一个争论,那么我可以声明如下:
template<template<typename> class T>
struct S {
T<int> t_;
//other code here
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我以后想要提供一个模板模板参数,该参数需要两个参数,其中第二个具有默认值(如std :: vector)T<int> t_;仍然有效,但模板不匹配template<typename> class T.我可以通过制作template<typename> class T一个可变参数模板模板来解决这个问题template<typename...> class T.现在我的代码更灵活了.
我将来可以将所有模板模板参数变为可变参数吗?有什么理由我不应该(因为其他原因我的代码已经需要C++ 11支持)?
这是合法的C++吗?
template <typename T, template <typename T> class>
struct S { };
Run Code Online (Sandbox Code Playgroud)
Clang(3.7.1)拒绝它,抱怨第一个第二个T阴影T.海湾合作委员会似乎不关心它,我认为这是合理的.我认为只有模板模板参数中重要的参数数量.
是否有一种直接的方法来获得可变参数的可变参数模板模板参数.例如,考虑以下函数签名
template<template<typename,size_t...> class Pack,
typename T, size_t ... Args>
void foo(const Pack<T,Args...>& a);
Run Code Online (Sandbox Code Playgroud)
如果我们想要传递两个Packs,我们现在必须做一个重载
template<template<typename,size_t...> class Pack,
typename T, size_t ... Args0, size_t ... Args1>
void foo(const Pack<T,Args0...>& a, const Pack<T,Args1...>& b);
Run Code Online (Sandbox Code Playgroud)
现在如果我们想要传递Pack具有不同可变参数的可变数量的对象,例如Args0...,Args1...,Args2....
所以我在想是否有一种切实可行的做法(以下是草图表示).
template<template<typename,size_t...> ... class Pack, typename T,...>
void foo(const Pack<T,...> ... packs);
Run Code Online (Sandbox Code Playgroud) c++ variadic-functions template-templates variadic-templates c++11
上下文
这个问题询问是否可以编写一个可以从给定实例中检索模板的特征。简而言之,问题是:“是否有可能写出f这样f<foo<int>>::type的foo?”
这个答案提出了一些我本来期望的正是那个问题所要求的东西,但答案已经提到它“不完全是”所要求的。
编码
为了查看运行中的代码并理解为什么它被标记为“不完全”,我写了这个:
#include <type_traits>
#include <iostream>
// the template
template <typename T> struct foo {};
// not the template
template <typename T> struct not_foo {};
template <template<class> class A, template<class> class B>
struct is_same_template : std::false_type {};
// this is the questionable part
template <template<class> class A>
struct is_same_template<A,A> : std::true_type {};
// copied from said answer
template <typename T> struct template_class;
template <template <typename> class C, typename T> …Run Code Online (Sandbox Code Playgroud) C++20 允许程序为模板模板参数指定概念。例如,
#include <concepts>
template <typename T> concept Char = std::same_as<T, char>;
template <typename> struct S {};
template <template <Char U> typename T, typename U> T<U> foo() { return {}; }
int main() { foo<S, int>(); }
Run Code Online (Sandbox Code Playgroud)
函数的第一个模板参数foo应该是一个单参数模板。
该概念Char仅被定义为 true 类型char,因此尝试满足它 forint将失败。以上程序被所有编译器接受:https : //gcc.godbolt.org/z/PaeETh6GP
能否请您解释一下,为什么可以指定模板模板参数中的概念,但仍然会被忽略?
在为另一个问题创建这个答案时,我遇到了以下问题。考虑这个程序(godbolt):
#include <variant>
#include <iostream>
template <typename T>
struct TypeChecker {
void operator()() {
std::cout << "I am other type\n";
}
};
template<typename... Ts, template<typename...> typename V>
requires std::same_as<V<Ts...>, std::variant<Ts...>>
struct TypeChecker<V<Ts...>>
{
void operator()()
{
std::cout << "I am std::variant\n";
}
};
int main()
{
TypeChecker<std::variant<int, float>>{}();
TypeChecker<int>{}();
}
Run Code Online (Sandbox Code Playgroud)
输出(也是预期的)如下(使用 clang 14.0.0 以及 gcc 12.1):
I am std::variant
I am other type
Run Code Online (Sandbox Code Playgroud)
然而,如果模板的参数列表中的三个点被删除,就像这样(整个程序运行在godbolt上):
template<typename... Ts, template<typename> typename V>
Run Code Online (Sandbox Code Playgroud)
,那么 clang 和 gcc 的输出是不同的。clang …
c++ templates partial-specialization template-templates language-lawyer
template<template<auto> class> struct A {};
template<int&> struct B {};
A<B> a;
int main() {}
Run Code Online (Sandbox Code Playgroud)
所有三个编译器 MSVC、GCC 和 Clang 的最新版本都接受此代码 ( https://godbolt.org/z/b7Pv7Ybxv )。但是,auto无法推断出引用类型,那么为什么允许这样做呢?或者编译器在查看标准内容时是否错误地接受了它?
我混淆了上面的参数和参数。我的目的是想问
template<template<int&> class> struct A {};
template<auto> struct B {};
A<B> a;
int main() {}
Run Code Online (Sandbox Code Playgroud)
这也被上述所有编译器所接受。然而,最初的示例也很有趣,因为模板模板参数应该至少与模板参数一样专业,但直观上看起来并不如此。
c++ template-templates language-lawyer auto non-type-template-parameter
c++ ×10
templates ×7
c++11 ×2
auto ×1
c++-concepts ×1
c++20 ×1
non-type-template-parameter ×1
shadowing ×1