提问人:Nick Williams 提问时间:10/1/2023 最后编辑:Nick Williams 更新时间:10/3/2023 访问量:130
如果可能enable_if_t如何将模板限制应用于具有单独实现代码的整个类?
How can you apply enable_if_t template restriction to entire class with separated implementation code, if possible?
问:
注意:我更喜欢始终将我的实现与我的声明分开,即使对于仍必须放在标头中的模板代码也是如此。因此,我倾向于为声明提供一个文件,并在该文件的底部包含一个用于内联和模板实现的文件,然后将我的非内联、非模板实现放在文件中。在这种情况下,可能无法遵循这种偏好,或者我可能做错了什么。.h
.hpp
.cpp
我有这个初始代码,它编译并运行:
#include <iostream>
#include <memory>
#include <string>
#define OddSource_Export __attribute((visibility("default")))
class IPAddress {};
class IPv4Address : public IPAddress {
public:
uint8_t version() const {
return 4;
}
};
class IPv6Address : public IPAddress {
public:
uint8_t version() const {
return 6;
}
};
template<class TAddress>
class OddSource_Export InterfaceIPAddress
{
public:
InterfaceIPAddress(TAddress const &, uint16_t);
uint8_t version() const;
private:
::std::unique_ptr<TAddress> const _address;
uint16_t const _flags;
};
template<class TAddress>
InterfaceIPAddress<TAddress>::
InterfaceIPAddress(TAddress const &address, uint16_t flags)
: _address(new TAddress(address)),
_flags(flags)
{}
template<class TAddress>
uint8_t
InterfaceIPAddress<TAddress>::
version() const
{
return this->_address->version();
}
int main()
{
IPv4Address addr4;
InterfaceIPAddress<IPv4Address> address(addr4, 0);
std::cout << "Version: " << std::to_string(address.version()) << std::endl;
return 0;
}
但是我想要一个简单的基类限制。因此,我首先定义了这个别名:
template<class TAddress>
using Enable_If_IPAddress = ::std::enable_if_t<::std::is_base_of_v<IPAddress, TAddress>>;
更改我的类声明,如下所示:
template<class TAddress, typename = Enable_If_IPAddress<TAddress>>
class OddSource_Export InterfaceIPAddress
并更改我的构造和方法实现如下:
template<class TAddress, typename>
这是新版本:
#include <iostream>
#include <memory>
#include <string>
#include <type_traits>
#define OddSource_Export __attribute((visibility("default")))
class IPAddress {};
class IPv4Address : public IPAddress {
public:
uint8_t version() const {
return 4;
}
};
class IPv6Address : public IPAddress {
public:
uint8_t version() const {
return 6;
}
};
template<class TAddress>
using Enable_If_IPAddress = ::std::enable_if_t<::std::is_base_of_v<IPAddress, TAddress>>;
template<class TAddress, typename = Enable_If_IPAddress<TAddress>>
class OddSource_Export InterfaceIPAddress
{
public:
InterfaceIPAddress(TAddress const &, uint16_t);
uint8_t version() const;
private:
::std::unique_ptr<TAddress> const _address;
uint16_t const _flags;
};
template<class TAddress, typename>
InterfaceIPAddress<TAddress>::
InterfaceIPAddress(TAddress const &address, uint16_t flags)
: _address(new TAddress(address)),
_flags(flags)
{}
template<class TAddress, typename>
uint8_t
InterfaceIPAddress<TAddress>::
version() const
{
return this->_address->version();
}
int main()
{
IPv4Address addr4;
InterfaceIPAddress<IPv4Address> address(addr4, 0);
std::cout << "Version: " << std::to_string(address.version()) << std::endl;
return 0;
}
但是,现在我的构造函数无法编译:
warning: missing 'typename' prior to dependent type name InterfaceIPAddress<TAddress>::InterfaceIPAddress; implicit 'typename' is a C++20 extension [-Wc++20-extensions]
InterfaceIPAddress<TAddress>::
^
typename
error: expected ')'
InterfaceIPAddress(TAddress const &address, uint16_t flags)
^
note: to match this '('
InterfaceIPAddress(TAddress const &address, uint16_t flags)
^
error: expected ';' at end of declaration
: _address(new TAddress(address)),
^
;
error: no template named '_address'; did you mean 'TAddress'?
: _address(new TAddress(address)),
^~~~~~~~
TAddress
该方法失败并出现类似的错误。这是在 Clang14 上。GCC 也有各种编译器错误,但它们是不同的:version()
error: invalid use of incomplete type ‘class InterfaceIPAddress’
38 | InterfaceIPAddress(TAddress const &address, uint16_t flags)
| ^
main.cpp:26:24: note: declaration of ‘class InterfaceIPAddress’
26 | class OddSource_Export InterfaceIPAddress
| ^~~~~~~~~~~~~~~~~~
我很确定我定义正确,因为如果我移动我的实现以与声明内联,它会在没有警告或错误的情况下编译。所以这有效:enable_if_t
#include <iostream>
#include <memory>
#include <string>
#include <type_traits>
#define OddSource_Export __attribute((visibility("default")))
class IPAddress {};
class IPv4Address : public IPAddress {
public:
uint8_t version() const {
return 4;
}
};
class IPv6Address : public IPAddress {
public:
uint8_t version() const {
return 6;
}
};
template<class TAddress>
using Enable_If_IPAddress = ::std::enable_if_t<::std::is_base_of_v<IPAddress, TAddress>>;
template<class TAddress, typename = Enable_If_IPAddress<TAddress>>
class OddSource_Export InterfaceIPAddress
{
public:
InterfaceIPAddress(TAddress const & address, uint16_t flags)
: _address(new TAddress(address)),
_flags(flags)
{}
uint8_t version() const
{
return this->_address->version();
}
private:
::std::unique_ptr<TAddress> const _address;
uint16_t const _flags;
};
int main()
{
IPv4Address addr4;
InterfaceIPAddress<IPv4Address> address(addr4, 0);
std::cout << "Version: " << std::to_string(address.version()) << std::endl;
return 0;
}
那么,我是否遗漏了一些需要为分离实现执行的额外步骤?还是在使用时无法分离此实现?enable_if_t
此外,我尝试在编译器建议的地方添加一个,但所做的只是满足警告。未解决编译错误。typename
编辑:这应该重新打开
我不明白为什么这个问题被关闭了。它说,“编辑问题以包括所需的行为、特定问题或错误,以及重现问题所需的最短代码。这些编辑已经完成,这个问题不可能比现在更清楚了。另外,它已经有一个完整且公认的答案。关闭这个问题是荒谬的,并且阻碍了其他人找到有用信息的能力。
答:
由于您为类添加了额外的模板参数,因此需要将其传递到实现声明中,例如
template<class TAddress, typename Enable>
InterfaceIPAddress<TAddress, Enable>::
InterfaceIPAddress(TAddress const &address, uint16_t flags)
: _address(new TAddress(address)),
_flags(flags)
{
}
使用简化的示例进行演示。
但是,对于您的示例,仅使用是更合适的选项,它消除了额外的模板并提供更好的诊断:static_assert
template<class TAddress>
class OddSource_Export InterfaceIPAddress
{
static_assert(std::is_base_of_v<IPAddress, TAddress>,
"the template parameter TAddress must derived from IPAddress");
public:
// ...
};
评论
, typename Enable>
Enable
InterfaceIPAddress<TAddress, Enable>::InterfaceIPAddress
InterfaceIPAddress<TAddress>::
InterfaceIPAddress<TAddress, Enable>::
std::enable_if_t
static assert
enable_if
requires
子句所取代。它甚至比 更干净,例如: 。static_assert
template<class TAddress> requires std::is_base_of_v<IPAddress, TAddress> class InterfaceIPAddress { ... }
评论
typename
// main.cpp
// A.h
// A.hpp
// A.cpp