作为我上一个问题的后续,我试图检测是否存在需要显式特化的模板函数.
我当前的工作代码检测非模板函数(感谢DyP的帮助),前提是它们至少使用一个参数,以便可以使用从属名称查找:
// switch to 0 to test the other case
#define ENABLE_FOO_BAR 1
namespace foo {
#if ENABLE_FOO_BAR
int bar(int);
#endif
}
namespace feature_test {
namespace detail {
using namespace foo;
template<typename T> decltype(bar(std::declval<T>())) test(int);
template<typename> void test(...);
}
static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;
static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");
}
Run Code Online (Sandbox Code Playgroud)
(ENABLE_FOO_BAR
宏仅用于测试目的,在我的实际代码中我没有这样的宏可用,否则我不会使用SFINAE)
当模板函数的模板参数可以由编译器自动推导时,这也适用于模板函数:
namespace foo {
#if ENABLE_FOO_BAR
template<typename T> int bar(T);
#endif
}
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试检测需要显式特化的模板函数时,存在时才会static_assert
启动:foo::bar()
namespace …
Run Code Online (Sandbox Code Playgroud) 嗨,我在选择具有明确特化的模板化类的正确版本时遇到问题.我想要使用用于专门化的类的派生类来选择特化.场景是:
#include <stdio.h>
class A
{};
class B: public A
{};
template<typename T>
class Foo
{
public:
int FooBar(void) { return 10; }
};
// Explicit specialization for A
template<> int Foo< A >::FooBar( void ) { return 20; }
void main( void)
{
Foo<B> fooB;
// This prints out 10 instead of wanted 20 ie compiler selects the general version
printf("%d", fooB.FooBar() );
}
Run Code Online (Sandbox Code Playgroud)
正如我在评论中所说的那样,我希望看到20被打印出来,因为B是从A派生出来的,而10则是打印出来的.如何在不诉诸为每个派生类编写专门化的情况下调用专门化(我的实际场景有很多派生类型).
(注意:我知道这是非法的,我正在寻找这种语言的原因.)
template<class c> void Foo(); // Note: no generic version, here or anywhere.
int main(){
Foo<int>();
return 0;
}
template<> void Foo<int>();
Run Code Online (Sandbox Code Playgroud)
错误:
error: explicit specialization of 'Foo<int>' after instantiation
Run Code Online (Sandbox Code Playgroud)
谷歌的快速传递发现了这个规范的引用,但这只提供了什么,而不是原因.
编辑:
一些回复转发了这一论点(例如证实了我的推测),即规则是这样的,因为否则会违反一个定义规则(ODR).然而,这是一个非常弱的论点,因为在这种情况下,它不适用于两个原因:
Foo<int>
不能定义模板的泛型特化,因为没有专用的泛型体.关于此事的猜测:
关于为什么规则存在的猜测:如果第一行提供了一个定义(而不是一个声明),实例化后的显式特化将是一个问题,因为你会得到多个定义.但在这种情况下,唯一的定义是明确的专业化.
奇怪的是,以下(或者我正在处理的实际代码中的类似内容)有效:
文件A:
template<class c> void Foo();
int main(){
Foo<int>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
档案B:
template<class c> void Foo();
template<> void Foo<int>();
Run Code Online (Sandbox Code Playgroud)
但总的来说,使用它开始创造一个意大利面条进口结构.
c++ templates language-specifications explicit-specialization
One Definition Rule规定程序应包含每个非内联函数的一个定义.对于模板类的成员,这对我来说并不完全清楚:
///////////
// Tfoo.h
template<typename T> class A {
void foo(){}
};
///////////
// intfoo.h
#include <Tfoo.h>
template<> class Foo<int> {
void foo(); // declaration only
};
/*inline*/ void Foo<int>::foo(){} // definition
///////////
// X.cpp
#include <intfoo.h>
///////////
// Y.cpp
#include <intfoo.h>
Run Code Online (Sandbox Code Playgroud)
在这种情况下,clientX.obj和clientY.obj都有一个定义Foo<int>::foo
.链接器抱怨此符号定义不止一次:
Y.obj : error LNK2005: "private: void __thiscall Foo<int>::foo(void)"
(?foo@?$Foo@H@@AAEXXZ) already defined in X.obj
Run Code Online (Sandbox Code Playgroud)
当我在前面inline
定义时Foo<int>::foo()
,一切顺利,链接器很高兴.当我在单独的编译单元中定义它们时(例如intfoo.cpp).
(注意:此解决方案在/sf/answers/103725751/中提出)
可能是一种误解,但模板类的成员函数总是"内联"?这里的规则是什么?
c++ templates linker-errors one-definition-rule explicit-specialization
以下设计可能吗?:
template <typename T>
class Test{
public:
template <typename Z>
void doSomething();
//rest of things
private:
T obj;
//some things
};
Run Code Online (Sandbox Code Playgroud)
现在,如果可能的话,我会为doSomething做一些明确的专业化,以便最后我会有一些版本如下:
void doSomething<int>(){
//do something
}
void doSomething<double>(){
//do something
}
...etc
Run Code Online (Sandbox Code Playgroud)
这似乎不可能我找不到任何语法来完成这项工作然后我想也许设计应该如下所示,以便所有模板参数应该传递给模板类本身:
template <typename T,typename Z>
class Test{
public:
void doSomething();
//rest of things
private:
T obj;
//some things
};
Run Code Online (Sandbox Code Playgroud)
然后我尝试了部分特化,甚至没有编译:
template <typename T>
void Test<T,int>::doSomething(){
//do something
}
template <typename T>
void Test<T,double>::doSomething(){
//do something
}
...etc
Run Code Online (Sandbox Code Playgroud)
我为显式特化得到了以下错误:
错误#1:类模板名称后面的模板参数列表必须按照模板参数列表中使用的顺序列出参数.
错误#2:'Container1':模板参数太少.
考虑以下代码:
#include<concepts>
template<typename>
void foo() { }
template<std::integral>
void foo() { }
template<>
void foo<bool>() { }
int main() { foo<bool>(); }
Run Code Online (Sandbox Code Playgroud)
它在最近的 Clang 和 MSVC 下编译没有问题,但在 GCC 下编译没有问题(godbolt 链接)
使用 GCC 构建失败并显示:
error: ambiguous template specialization 'foo<bool>' for 'void foo()'
9 | void foo<bool>() { }
| ^~~~~~~~~
note: candidates are: 'template<class> void foo()'
3 | void foo() { }
| ^~~
note: 'template<class> requires integral< <template-parameter-1-1> > void foo()'
6 | void foo() { }
| …
Run Code Online (Sandbox Code Playgroud) c++ template-specialization language-lawyer explicit-specialization c++-concepts
为了防止在数百个文件中进行冗余实例化,我有了(似乎)明确extern template class std::shared_ptr<SomeWidelyUsedClass>
在stdafx.h 中使用的好主意,想象我可以放在单个.cpp中以强制单个实例化并希望保存在编译中/链接时间.但是,检查生成的.cod和.obj文件表明无论如何都会在任何地方创建代码.但是,如果我使用与我自己的模板类完全相同的技术,它会按预期工作.有什么特别之处可以排除这种用途吗?也许某些东西本身会迫使编译器在它到达我的语句之前创建一个实例化(我很确定在stdafx.h中没有更高的东西可以使用)?#include <memory>
std::shared_ptr<SomeWidelyUsedClass>
template class std::shared_ptr<SomeWidelyUsedClass>
shared_ptr<SomeWidelyUsedClass>
shared_ptr
<memory>
extern template
shared_ptr
澄清:
// stdafx.h; included in every cpp in the project
#include <memory>
#include "SomeWidelyUsedClass.h" // no shared_ptr in here
// I expect this to prevent instantiation of std::shared_ptr<SomeWidelyUsedClass>
// in all compilation units that include this, except the one below.
extern template class std::shared_ptr<SomeWidelyUsedClass>;
Run Code Online (Sandbox Code Playgroud)
然后:
// ExplicitTemplateInstantiations.cpp
#include "stdafx.h"
// I expect this to cause std::shared_ptr<SomeWidelyUsedClass>
// to be instantiated in …
Run Code Online (Sandbox Code Playgroud) c++ templates visual-c++ explicit-specialization visual-studio-2012
代码
template <typename T>
void foo(const T& t)
{}
template <typename T>
class A
{
template <>
friend void foo<T>(const T& t)
{}
};
Run Code Online (Sandbox Code Playgroud)
给出编译错误
"defining explicit specialization ‘foo<T>’ in friend declaration friend void foo<T>(const T& t)"
Run Code Online (Sandbox Code Playgroud)
用gcc和
"error C3637: 'A<int>::foo' : a friend function definition cannot be a specialization of a unction template"
Run Code Online (Sandbox Code Playgroud)
在VS2013中编译时
我了解该标准是这样说的,但是为什么呢?我想了解原因(在幕后),有很多文章写着“显式专业化不能成为朋友声明。”,但我不明白为什么。有任何想法吗?
考虑到这段代码:
template <class T>
void f(T p) { //(1)
cout << "Second" << endl;
}
template <>
void f(int *p) { //(2)
cout << "Third" << endl;
}
template <class T>
void f(T* p) { //(3)
cout << "First" << endl;
}
Run Code Online (Sandbox Code Playgroud)
诸如此类的调用int *p; f(p);
将输出First
.
如果声明的顺序改变了,像这样:
template <class T>
void f(T* p) { //(3)
cout << "First" << endl;
}
template <class T>
void f(T p) { //(1)
cout << "Second" << endl;
}
template <> …
Run Code Online (Sandbox Code Playgroud) 类似这个问题有关模板类的静态常量类成员明确分工,以及这个问题对模板类的明确分工,但我的问题是具有可变模板的显式特。
我的 MCVE:
//my_templated_literal.h
#pragma once
template <typename T>
constexpr T val;
Run Code Online (Sandbox Code Playgroud)
//my_specialised_literal.h
#pragma once
#include "my_templated_literal.h"
template <>
constexpr int val<int> = 2;
Run Code Online (Sandbox Code Playgroud)
//my_specialised_literal.h
#pragma once
#include "my_templated_literal.h"
template <>
constexpr int val<int> = 2;
Run Code Online (Sandbox Code Playgroud)
//main.cc
#include "my_specialised_literal.h"
int main() {}
Run Code Online (Sandbox Code Playgroud)
编译命令:$CXX -std=c++14 my_specialised_literal.cc main.cc
这可以编译并且似乎在我尝试过的几乎每个编译器版本上都按预期工作,但是会在使用 clang-9 时出现链接器错误:
/tmp/main-ec49c7.o:(.rodata+0x0):`val'的多重定义
/tmp/my_specialised_literal-521691.o:(.rodata+0x0): 这里首先定义
这是大多数编译器版本默默接受的 ODR 违规,还是 clang-9 在某种程度上是错误的?如果是前者,我知道如果我可以使用 C++17,我可以通过进行 specialization 来修复它inline
,但是什么是 C++14 修复问题?