考虑一个例子:
#include <type_traits>
#include <string>
template <template <class> class TT> //#1
struct Foo {
static void foo() {
static_assert(std::is_same_v<decltype(TT("abc")), TT<std::string>>);
}
};
template <class T>
struct Bar {
Bar(T) {}
};
template <class T>
Bar(T) -> Bar<std::string>; //#2
int main() {
Foo<Bar>::foo();
}
Run Code Online (Sandbox Code Playgroud)
在推导模板模板参数(#1)的模板参数时,[clang]和[gcc]似乎都使用用户提供的演绎指南(#2).它是标准兼容功能吗?
c++ templates language-lawyer template-argument-deduction c++17
我得到的最小例子有点复杂:
struct A { };
template <int>
struct Parent { };
template <int N>
constexpr int operator*(A, Parent<N>*) { return N; }
template <class T>
using ptr = T*;
template <int>
struct Other { };
template <int N>
struct Kid: Parent<N> {
static Other<A{} * ptr<Kid>{}> o;
};
int main() {
Kid<2>{};
}
Run Code Online (Sandbox Code Playgroud)
[GCC]编译代码而没有任何问题,[铛]抱怨匹配Parent
针对Kid
问题:
prog.cc:7:15: note: candidate template ignored: could not match 'Parent' against 'Kid'
constexpr int operator*(A, Parent<N>*) { return N; } …
Run Code Online (Sandbox Code Playgroud) c++ language-lawyer incomplete-type constexpr template-argument-deduction
除了对构造函数最明显的模板参数推导使用外,我可以想象一些更复杂的用例,我们只推导出模板类的部分参数,例如:
std::pair<int> p(1, 2); // std::pair<int, int>
Run Code Online (Sandbox Code Playgroud)
虽然这个结构是在函数中推导出模板参数的自然结果,但我找不到这种用法的任何例子.也许是因为具有可变参数模板参数的类的模糊性?
std::tuple<int> t(1, 2, 3); // std::tuple<int, int, int>
Run Code Online (Sandbox Code Playgroud)
然而,这种方式引入语法不会替代太好的"make_*"包装器(参见N3602),其中功能可供我们使用...
这是这个问题的延续.如果成员类的部分特化是这样的,我特别感兴趣:
struct FooParent {
template <class>
struct Bar{ };
};
struct Foo: FooParent {
template <class T>
struct Bar<T*> {};
};
Run Code Online (Sandbox Code Playgroud)
我知道这可以在命名空间范围内完成:
template <class T>
struct Foo::Bar<T*>{ };
Run Code Online (Sandbox Code Playgroud)
但我也特别感兴趣的是派生类级别的类内部分特化.
clang和gcc在遇到前者时都会抱怨:
clang声明有明确的模板专业化,显然不会发生:
错误:类范围中'Bar'的显式特化
gcc在这里稍微冗长一点,并说成员模板的特化必须在命名空间范围内执行,这显然不是非派生类的情况.
错误:'template struct FooParent :: Bar'的特化必须出现在命名空间范围内
gcc是否在他的错误消息中?
c++ templates partial-specialization template-specialization language-lawyer
在使用ADL解决这个问题后,如果传递的类型来自我们的命名空间,则可以创建一个特征来回答:
#include <utility>
namespace helper
{
template <typename T, typename = void>
struct is_member_of_sample : std::false_type
{
};
template <typename T>
struct is_member_of_sample<
T,
decltype(adl_is_member_of_sample(std::declval<T>()))> : std::true_type
{
};
}
namespace sample
{
template <typename T>
auto adl_is_member_of_sample(T && ) -> void;
}
// -- Test it
namespace sample
{
struct X;
}
struct Y;
static_assert(helper::is_member_of_sample<sample::X>::value, "");
static_assert(not helper::is_member_of_sample<Y>::value, "");
int main(){}
Run Code Online (Sandbox Code Playgroud)
由于显而易见的原因,这不能应用于std
命名空间 - 根本没有办法将adl_is_member_of_sample
等效命令注入std
命名空间而不将自己暴露给未定义的行为.
是否有一些解决方法可以创建特征?
为什么允许在gcc中编译模板版本?它是编译器错误还是与模板一起使用时实际上有效?有人可以向我解释一下吗?
它不能在godbolt.org上使用的clang或其他编译器上编译.
编译错误由constexpr中使用的字符串和字符串流生成.
#include <iostream>
#include <string>
#include <sstream>
template<typename T>
constexpr std::string func1(T a, T b) //Compiles and runs
{
std::stringstream ss;
ss << a << b << a+b;
return ss.str();
}
constexpr std::string func2(int a, int b) //Compile error
{
std::stringstream ss;
ss << a << b << a+b;
return ss.str();
}
int main()
{
int a = 5;
int b = 7;
std::cout << func1(a,b) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud) 在尝试创建特征以检查类字段是否公开可用时,我创建了一个代码:
#include <type_traits>
#include <utility>
template <class T, class = void>
struct does_not_have_foo: std::true_type {};
template <class T>
struct does_not_have_foo<T, decltype(std::declval<T>().foo, void())>: std::false_type {};
class Foo {
int foo;
};
int main() {
static_assert(does_not_have_foo<Foo>::value);
}
Run Code Online (Sandbox Code Playgroud)
但它似乎未能在[gcc]中编译([clang]似乎在这里更宽松)并出现错误:
prog.cc:8:56: error: 'int Foo::foo' is private within this context
Run Code Online (Sandbox Code Playgroud)
我为我的一些旧代码挖出了类似的东西,归结为以下内容:
template <class T>
auto does_not_have_foo(T t) -> decltype(t.foo, std::false_type()) {
return {};
}
std::true_type does_not_have_foo(...) { return {}; }
class Foo {
int foo;
};
int main() {
static_assert(decltype(does_not_have_foo(Foo{}))::value);
} …
Run Code Online (Sandbox Code Playgroud) 考虑一个简单的例子:
template <class T>
struct tag { };
int main() {
auto foo = [](auto x) -> decltype(bar(x)) { return {}; };
tag<int> bar(tag<int>);
bar(tag<int>{}); // <- compiles OK
foo(tag<int>{}); // 'bar' was not declared in this scope ?!
}
tag<int> bar(tag<int>) { return {}; }
Run Code Online (Sandbox Code Playgroud)
c++ templates language-lawyer argument-dependent-lookup c++14
根据cppreference部分核心常量表达式指出19)两个指针之间的减法运算符不是合法的常量表达式,直到c ++ 14.我可以假设以下代码是合法的c ++ 17代码或者这种解释是滥用吗?
int X, Y;
template <long long V>
struct S { };
int main() {
S<&X - &Y> s;
(void)s;
}
Run Code Online (Sandbox Code Playgroud) 根据cppreference(强调我的):
核心常量表达式是任何子表达式中没有以下任何一个的表达式
(...)
- 一种表达式,其评估会导致任何形式的核心语言未定义行为(包括有符号整数溢出,除零,数组边界外的指针运算等).是否未指定检测到标准库未定义行为.
另一方面,指针上有几个表达式,结果不是未定义但未指定(参见[expr.rel]/3),例如:
struct A {
int v;
};
struct B {
int v;
};
struct C: A, B {} c;
int main() {
constexpr bool result = &c.A::v < &c.B::v;
(void)result;
}
Run Code Online (Sandbox Code Playgroud)
代码编译没有gcc的问题,但没有在clang中编写,其中说明无疑是真的:
不同基类的子对象地址的比较尚未明确
但是(根据我的理解),根据cppreference它不应该阻止编译器编译代码.
哪个编译器就在这里 - gcc还是clang?我是否过度解释了cppreference?
c++ ×10
templates ×5
c++17 ×3
constexpr ×3
c++11 ×2
c++14 ×2
template-argument-deduction ×2
type-traits ×2
gcc ×1
sfinae ×1