C++ 模板专业化:参数不同于T的成员函数
是否可以使用模板专业化来实现以下内容C++ 模板专业化:参数不同于T的成员函数,c++,templates,template-specialization,C++,Templates,Template Specialization,是否可以使用模板专业化来实现以下内容 #include <iostream> template< typename T, bool a, bool b> class Test { T value; public: Test() {} Test( const T& val ) : value( val ) {} void Set( const T& val ) { // [1] value = val;
#include <iostream>
template< typename T, bool a, bool b>
class Test
{
T value;
public:
Test() {}
Test( const T& val ) : value( val ) {}
void Set( const T& val ) { // [1]
value = val;
}
void Set( const float val ) { // [2] To run just for T != float
...
}
};
int main()
{
Test<int, true, true> test1;
test1.Set( 1234 ); // Run [1]
Test<float, true, true> test2;
test2.Set( 1.234f ); // Run [1]
Test<int, true, true> test3;
test3.Set( 1.234f ); // Run [2]
}
当T不同于float时,是否有语法指定要选择的成员函数?一个干净的选项是使用非多态继承进行专门化,而不重复所有操作:
namespace detail
{
template< typename T, bool a, bool b>
class TestImplBase
{
protected:
T value;
public:
TestImplBase() {}
TestImplBase( const T& val ) : value( val ) {}
void Set( const T& val ) { // [1]
value = val;
}
};
// General case
template< typename T, bool a, bool b>
class TestImpl : public TestImplBase<T, a, b>
{
public:
using TestImplBase<T, a, b>::TestImplBase; // keep the same constructors
using TestImplBase<T, a, b>::Set; // See comment by Jarod42
// Has [1] as well.
void Set( const float val ) { // [2] To run just for T != float
//...
}
};
// Specialization for T == float
template<bool a, bool b>
class TestImpl<float, a, b> : public TestImplBase<float, a, b>
{
public:
using TestImplBase<float, a, b>::TestImplBase; // keep the same constructors
// Only has [1], not [2].
};
}
template< typename T, bool a, bool b>
using Test = detail::TestImpl<T, a, b>;
这是一种非常通用的方法,对于您的情况来说可能有点过于通用,但从这一小段代码中很难说出来。一个干净的选择是使用非多态继承来进行专门化,而不重复所有内容:
namespace detail
{
template< typename T, bool a, bool b>
class TestImplBase
{
protected:
T value;
public:
TestImplBase() {}
TestImplBase( const T& val ) : value( val ) {}
void Set( const T& val ) { // [1]
value = val;
}
};
// General case
template< typename T, bool a, bool b>
class TestImpl : public TestImplBase<T, a, b>
{
public:
using TestImplBase<T, a, b>::TestImplBase; // keep the same constructors
using TestImplBase<T, a, b>::Set; // See comment by Jarod42
// Has [1] as well.
void Set( const float val ) { // [2] To run just for T != float
//...
}
};
// Specialization for T == float
template<bool a, bool b>
class TestImpl<float, a, b> : public TestImplBase<float, a, b>
{
public:
using TestImplBase<float, a, b>::TestImplBase; // keep the same constructors
// Only has [1], not [2].
};
}
template< typename T, bool a, bool b>
using Test = detail::TestImpl<T, a, b>;
这是一种非常通用的方法,对于您的案例来说可能有点太通用了,但是从这一小段代码中很难说出来
是否可以使用模板专业化来实现以下内容
是:专门化完整类测试:泛型用例,带[2],浮点用例,不带[2]
否则,我想当T是float时,可以使用SFINAE禁用[2]
或者也可能
template <typename U = T>
std::enable_if_t<false == std::is_same_v<U, float> && true == std::is_same_v<U, T>>
Set( const float val )
{ /* ... */ }
如果要避免启用该集合,请在浮点情况下,说明U模板类型
是否可以使用模板专业化来实现以下内容
是:专门化完整类测试:泛型用例,带[2],浮点用例,不带[2]
否则,我想当T是float时,可以使用SFINAE禁用[2]
或者也可能
template <typename U = T>
std::enable_if_t<false == std::is_same_v<U, float> && true == std::is_same_v<U, T>>
Set( const float val )
{ /* ... */ }
如果您想避免启用该集合,在浮点情况下,解释U模板类型。使用C++20,它将非常简单: 需要允许放弃方法:
template <typename T, bool a, bool b>
class Test
{
T value;
public:
Test() {}
Test(const T& val) : value( val ) {}
void Set(const T& val) { value = val; }
void Set(float val) requires (!std::is_same<T, float>::value) {
// ...
}
};
否则,SFINAE通常是删除重载的方法,但需要模板函数,因此您必须制作一个方法模板。使用C++20,它将非常简单: 需要允许放弃方法:
template <typename T, bool a, bool b>
class Test
{
T value;
public:
Test() {}
Test(const T& val) : value( val ) {}
void Set(const T& val) { value = val; }
void Set(float val) requires (!std::is_same<T, float>::value) {
// ...
}
};
否则SFINAE通常是删除重载的方法,但需要模板功能,因此您必须制作一个方法模板。只是因为我在另一个选项卡中打开了它:。链接只回答这不是一个选项,但我假设在某个地方有一个重复。只是因为我在另一个选项卡中打开了它:。链接只回答这不是一个选项,但我认为有一个重复的地方。它的工作!问题:为什么我不能再为函数指定返回类型?另一个问题:在类块外实现函数的语法是什么?@Pietro必须将返回类型指定为std::enable_if_t的第二个参数。请参阅我在评论中发布的链接,并查看该链接以了解为什么这不是首选解决方案。@Pietro是的,但如果尚未启用,则这是默认设置。虽然我想显式地指定它并没有什么坏处。@Pietro您需要两个模板,是的。您还需要重复所有SFINAE的内容,因为它是一个模板,所以它必须在标题中。因此,我认为在类之外定义函数并不会给你带来太多好处,你只需要重复所有签名的内容就行了。它是有效的!问题:为什么我不能再为函数指定返回类型?另一个问题:在类块外实现函数的语法是什么?@Pietro必须将返回类型指定为std::enable_if_t的第二个参数。请参阅我在评论中发布的链接,并查看该链接以了解为什么这不是首选解决方案。@Pietro是的,但如果尚未启用,则这是默认设置。虽然我想显式地指定它并没有什么坏处。@Pietro您需要两个模板,是的。您还需要重复所有SFINAE的内容,因为它是一个模板,所以它必须在标题中。因此,我认为在类之外定义函数并不会给您带来太多好处,您只需重复所有签名内容。几乎,但您调用了错误的重载,因为[2]隐藏了[1]。您需要使用TestImplBase::Set@Jarod42,谢谢您发现了这一点!更正了答案。接近,但您调用了错误的重载,因为[2]隐藏了[1]。您需要使用TestImplBase::Set@Jarod42,谢谢您发现了这一点!更正了答案。很好的简化。很好的简化。