使用=delete 进行接口描述
Using = delete for interface description
我正在尝试为一个自由函数 listenTo(SomeAnimal)
提供一个接口描述,该函数应该对满足特定类型要求的类型进行操作(它应该是一种动物)。函数参数不应使用纯虚方法的接口继承机制。
我破解了一个解决方案,其中自由函数通过 sfinae 语句检查参数类型以获得基 class。为了保证参数实现基本 class 的接口,我使用 = delete
删除了基本 class 方法。我没有在互联网上找到任何类似的解决方案,因此,我不确定它是否有意义,但它确实有效。
给了,有什么意见吗?
#include <iostream>
#include <type_traits>
class IAnimal {
public:
// Interface that needs to be implemented
std::string sound() const = delete;
protected:
IAnimal(){}
};
class Cat : public IAnimal {
public:
// Implements deleted method
std::string sound() const {
return std::string("Meow");
}
};
class WildCat : public Cat {
public:
// Overwrites Cat sound method
std::string sound() const {
return std::string("Rarr");
}
};
class Dog : public IAnimal{
public:
// Implements deleted method
std::string sound() const {
return std::string("Wuff");
}
};
class Car {
public:
// Implements deleted method
std::string sound() const {
return std::string("Brum");
}
};
// Sfinae tests for proper inheritance
template<class TAnimal,
typename = std::enable_if_t<std::is_base_of<IAnimal, TAnimal>::value> >
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
int main(){
// Objects of type IAnimal can not be instanciated
// IAnimal a;
// Cats and Dogs behave like IAnimals
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
// listenTo(car);
return 0;
}
另一种避免继承复杂化的方法是创建类型特征:
#include <iostream>
#include <type_traits>
template<class T>
struct is_animal : std::false_type {};
class Cat {
public:
std::string sound() const {
return std::string("Meow");
}
};
template<> struct is_animal<Cat> : std::true_type {};
class WildCat : public Cat {
public:
// Overwrites Cat sound method
std::string sound() const {
return std::string("Rarr");
}
};
template<> struct is_animal<WildCat> : std::true_type {};
class Dog {
public:
std::string sound() const {
return std::string("Wuff");
}
};
template<> struct is_animal<Dog> : std::true_type {};
class Car {
public:
std::string sound() const {
return std::string("Brum");
}
};
// Sfinae tests for proper inheritance
template<class TAnimal,
typename = std::enable_if_t<is_animal<TAnimal>::value> >
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
int main(){
// Objects of type IAnimal can not be instanciated
// IAnimal a;
// Cats and Dogs behave like IAnimals
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
// listenTo(car);
return 0;
}
namespace details {
template<template<class...>class Z, class always_void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
这是一个有助于编写其他类型特征的元类型特征。
template<class T>
using sound_result = decltype( std::declval<T>().sound() );
sound_result<T>
是 t.sound()
的结果,其中 t
的类型是 T
.
template<class T>
using can_sound = can_apply<sound_result, T>;
can_sound<T>
是真正的类型当且仅当 t.sound()
有效调用。
我们现在可以说动物是会发声的东西。
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<class T>
using is_animal = bool_t< can_sound<T>{} >; // add more requirements
template<class TAnimal,
std::enable_if_t< is_animal<TAnimal const&>{}, int> =0
>
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
如果我们尝试 listenTo(0)
或类似的方法,我们会收到一条错误消息,提示没有匹配的重载。
要求.sound()
return 也可以编写可流式传输的内容。
template<class T>
using stream_result = decltype( std::declval<std::ostream&>() << std::declval<T>() );
template<class T>
using can_stream = can_apply< stream_result, T >;
template<class T>
using stream_sound_result = stream_result< sound_result< T > >;
template<class T>
using can_stream_sound = can_apply< stream_sound_result, T >;
现在我们可以升级我们的动物测试了:
template<class T>
using is_animal = bool_t< can_stream_sound<T>{} >;
您没有要求替代解决方案。相反,您征求了对您的解决方案的意见。
好了,以上就是我的看法,希望能帮到你。
这是一个虚弱的 sfinae 表达。您可以使用以下方式轻松破解它:
listenTo<Car, void>(car);
至少,我建议您按如下方式重写您的函数:
template<class TAnimal>
std::enable_if_t<std::is_base_of<IAnimal, TAnimal>::value>
listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
也就是说,就目前而言,您真的不需要使用 std::enable_if_t
或任何其他 sfinae 表达式。
在这种情况下,一个 static_assert
就足够了:
template<class TAnimal>
void listenTo(TAnimal const & a ) {
static_assert(std::is_base_of<IAnimal, TAnimal>::value, "!");
std::cout << a.sound() << std::endl;
}
这样你也可以从 IAnimal
中删除 sound
的无用定义,但你仍然会遇到一个很好的编译错误。
现在,如果您还想删除 IAnimal
界面,可能的解决方案(其他答案未提及)如下:
#include <iostream>
#include <type_traits>
template<typename> struct tag {};
template<typename... T> struct check;
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
template<>
struct check<> {
template<typename T>
static constexpr bool verify(tag<T>) { return false; }
};
class Cat {
public:
std::string sound() const { return std::string("Meow"); }
};
class WildCat {
public:
std::string sound() const { return std::string("Rarr"); }
};
class Dog {
public:
std::string sound() const { return std::string("Wuff"); }
};
class Car {
public:
std::string sound() const { return std::string("Brum"); }
};
using AnimalCheck = check<Cat, WildCat, Dog>;
template<class TAnimal>
void listenTo(TAnimal const & a ) {
static_assert(AnimalCheck::verify(tag<TAnimal>{}), "!");
std::cout << a.sound() << std::endl;
}
int main(){
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
//listenTo(car);
return 0;
}
根据评论中的要求,您可以集中检查check
class中要调用的方法是否存在。
例如:
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
static constexpr auto test()
-> decltype(std::declval<T>().sound(), bool{})
{ return true; }
static_assert(test(), "!");
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
或更紧凑的版本:
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
static_assert(decltype(std::declval<T>().sound(), std::true_type{}){}, "!");
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
这是一种通过仅使用当前语言修订版中的功能来检查概念的方法。
请注意,概念 会以某种方式在代码中的某处帮助执行相同的操作,但它们还不是标准的一部分。
delete
ing 一个函数会删除它,它不会引入对它的依赖。它说 "this class does not have this function"。因此,就 implementing/annotating 界面而言,这是实现目标的一种奇怪方式。这有点像构建一个完整的驾驶舱 F-32 模拟器并告诉一个非常困惑的第一位试飞员 "well we removed all the buttons so you'll know what actually exists in a real plane".
在 C++ 中实现接口的方式是使用虚函数,你可以通过给它们一个“0”的主体来将虚函数注释为 "pure"(待实现),就像这样:
struct IFace {
virtual void sound() = 0;
};
这使得无法创建 IFace
或任何派生自它的 class 的具体实例,直到您到达实现了 sound()
的层次结构的一部分:
struct IAudible {
virtual void sound() const = 0;
};
struct Explosion : public IAudible {
// note the 'override' keyword, optional but helpful
virtual void sound() const override { std::cout << "Boom\n"; }
};
struct Word : public IAudible {
};
void announce(const IAudible& audible) {
audible.sound();
}
int main() {
Explosion e;
announce(e);
}
但是如果我们尝试实例化"Word",我们会得到一个编译器错误:http://ideone.com/jriyay
prog.cpp: In function 'int main()':
prog.cpp:21:14: error: cannot declare variable 'w' to be of abstract type 'Word'
Word w;
prog.cpp:11:12: note: because the following virtual functions are pure within 'Word':
struct Word : public IAudible {
^
prog.cpp:4:22: note: virtual void IAudible::sound() const
virtual void sound() const = 0;
C++ 还没有 Concepts :-( 但 gcc-6 实现了它:
template <class T>
concept bool Animal() {
return requires(const T& a) {
{a.sound()} -> std::string;
};
}
void listenTo(const Animal& animal) {
std::cout << animal.sound() << std::endl;
}
但是您可以使用 :
相对轻松地创建特征
typename <typename T>
using sound_type = decltype(std::declval<const T&>().sound());
template <typename T>
using has_sound = is_detected<sound_type, T>;
template <typename T>
using is_animal = has_sound<T>;
// or std::conditional_t<has_sound<T>::value /*&& other_conditions*/,
// std::true_type, std::false_type>;
然后是常规 SFINAE:
template<class T>
std::enable_if_t<is_animal<T>::value>
listenTo(const T& animal) {
std::cout << animal.sound() << std::endl;
}
我正在尝试为一个自由函数 listenTo(SomeAnimal)
提供一个接口描述,该函数应该对满足特定类型要求的类型进行操作(它应该是一种动物)。函数参数不应使用纯虚方法的接口继承机制。
我破解了一个解决方案,其中自由函数通过 sfinae 语句检查参数类型以获得基 class。为了保证参数实现基本 class 的接口,我使用 = delete
删除了基本 class 方法。我没有在互联网上找到任何类似的解决方案,因此,我不确定它是否有意义,但它确实有效。
给了,有什么意见吗?
#include <iostream>
#include <type_traits>
class IAnimal {
public:
// Interface that needs to be implemented
std::string sound() const = delete;
protected:
IAnimal(){}
};
class Cat : public IAnimal {
public:
// Implements deleted method
std::string sound() const {
return std::string("Meow");
}
};
class WildCat : public Cat {
public:
// Overwrites Cat sound method
std::string sound() const {
return std::string("Rarr");
}
};
class Dog : public IAnimal{
public:
// Implements deleted method
std::string sound() const {
return std::string("Wuff");
}
};
class Car {
public:
// Implements deleted method
std::string sound() const {
return std::string("Brum");
}
};
// Sfinae tests for proper inheritance
template<class TAnimal,
typename = std::enable_if_t<std::is_base_of<IAnimal, TAnimal>::value> >
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
int main(){
// Objects of type IAnimal can not be instanciated
// IAnimal a;
// Cats and Dogs behave like IAnimals
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
// listenTo(car);
return 0;
}
另一种避免继承复杂化的方法是创建类型特征:
#include <iostream>
#include <type_traits>
template<class T>
struct is_animal : std::false_type {};
class Cat {
public:
std::string sound() const {
return std::string("Meow");
}
};
template<> struct is_animal<Cat> : std::true_type {};
class WildCat : public Cat {
public:
// Overwrites Cat sound method
std::string sound() const {
return std::string("Rarr");
}
};
template<> struct is_animal<WildCat> : std::true_type {};
class Dog {
public:
std::string sound() const {
return std::string("Wuff");
}
};
template<> struct is_animal<Dog> : std::true_type {};
class Car {
public:
std::string sound() const {
return std::string("Brum");
}
};
// Sfinae tests for proper inheritance
template<class TAnimal,
typename = std::enable_if_t<is_animal<TAnimal>::value> >
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
int main(){
// Objects of type IAnimal can not be instanciated
// IAnimal a;
// Cats and Dogs behave like IAnimals
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
// listenTo(car);
return 0;
}
namespace details {
template<template<class...>class Z, class always_void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
这是一个有助于编写其他类型特征的元类型特征。
template<class T>
using sound_result = decltype( std::declval<T>().sound() );
sound_result<T>
是 t.sound()
的结果,其中 t
的类型是 T
.
template<class T>
using can_sound = can_apply<sound_result, T>;
can_sound<T>
是真正的类型当且仅当 t.sound()
有效调用。
我们现在可以说动物是会发声的东西。
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<class T>
using is_animal = bool_t< can_sound<T>{} >; // add more requirements
template<class TAnimal,
std::enable_if_t< is_animal<TAnimal const&>{}, int> =0
>
void listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
如果我们尝试 listenTo(0)
或类似的方法,我们会收到一条错误消息,提示没有匹配的重载。
要求.sound()
return 也可以编写可流式传输的内容。
template<class T>
using stream_result = decltype( std::declval<std::ostream&>() << std::declval<T>() );
template<class T>
using can_stream = can_apply< stream_result, T >;
template<class T>
using stream_sound_result = stream_result< sound_result< T > >;
template<class T>
using can_stream_sound = can_apply< stream_sound_result, T >;
现在我们可以升级我们的动物测试了:
template<class T>
using is_animal = bool_t< can_stream_sound<T>{} >;
您没有要求替代解决方案。相反,您征求了对您的解决方案的意见。
好了,以上就是我的看法,希望能帮到你。
这是一个虚弱的 sfinae 表达。您可以使用以下方式轻松破解它:
listenTo<Car, void>(car);
至少,我建议您按如下方式重写您的函数:
template<class TAnimal>
std::enable_if_t<std::is_base_of<IAnimal, TAnimal>::value>
listenTo(TAnimal const & a ) {
std::cout << a.sound() << std::endl;
}
也就是说,就目前而言,您真的不需要使用 std::enable_if_t
或任何其他 sfinae 表达式。
在这种情况下,一个 static_assert
就足够了:
template<class TAnimal>
void listenTo(TAnimal const & a ) {
static_assert(std::is_base_of<IAnimal, TAnimal>::value, "!");
std::cout << a.sound() << std::endl;
}
这样你也可以从 IAnimal
中删除 sound
的无用定义,但你仍然会遇到一个很好的编译错误。
现在,如果您还想删除 IAnimal
界面,可能的解决方案(其他答案未提及)如下:
#include <iostream>
#include <type_traits>
template<typename> struct tag {};
template<typename... T> struct check;
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
template<>
struct check<> {
template<typename T>
static constexpr bool verify(tag<T>) { return false; }
};
class Cat {
public:
std::string sound() const { return std::string("Meow"); }
};
class WildCat {
public:
std::string sound() const { return std::string("Rarr"); }
};
class Dog {
public:
std::string sound() const { return std::string("Wuff"); }
};
class Car {
public:
std::string sound() const { return std::string("Brum"); }
};
using AnimalCheck = check<Cat, WildCat, Dog>;
template<class TAnimal>
void listenTo(TAnimal const & a ) {
static_assert(AnimalCheck::verify(tag<TAnimal>{}), "!");
std::cout << a.sound() << std::endl;
}
int main(){
Cat cat;
WildCat wildCat;
Dog dog;
Car car;
listenTo(cat);
listenTo(wildCat);
listenTo(dog);
// A car is no animal -> compile time error
//listenTo(car);
return 0;
}
根据评论中的要求,您可以集中检查check
class中要调用的方法是否存在。
例如:
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
static constexpr auto test()
-> decltype(std::declval<T>().sound(), bool{})
{ return true; }
static_assert(test(), "!");
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
或更紧凑的版本:
template<typename T, typename... U>
struct check<T, U...>: check<U...> {
static_assert(decltype(std::declval<T>().sound(), std::true_type{}){}, "!");
using check<U...>::verify;
static constexpr bool verify(tag<T>) { return true; }
};
这是一种通过仅使用当前语言修订版中的功能来检查概念的方法。
请注意,概念 会以某种方式在代码中的某处帮助执行相同的操作,但它们还不是标准的一部分。
delete
ing 一个函数会删除它,它不会引入对它的依赖。它说 "this class does not have this function"。因此,就 implementing/annotating 界面而言,这是实现目标的一种奇怪方式。这有点像构建一个完整的驾驶舱 F-32 模拟器并告诉一个非常困惑的第一位试飞员 "well we removed all the buttons so you'll know what actually exists in a real plane".
在 C++ 中实现接口的方式是使用虚函数,你可以通过给它们一个“0”的主体来将虚函数注释为 "pure"(待实现),就像这样:
struct IFace {
virtual void sound() = 0;
};
这使得无法创建 IFace
或任何派生自它的 class 的具体实例,直到您到达实现了 sound()
的层次结构的一部分:
struct IAudible {
virtual void sound() const = 0;
};
struct Explosion : public IAudible {
// note the 'override' keyword, optional but helpful
virtual void sound() const override { std::cout << "Boom\n"; }
};
struct Word : public IAudible {
};
void announce(const IAudible& audible) {
audible.sound();
}
int main() {
Explosion e;
announce(e);
}
但是如果我们尝试实例化"Word",我们会得到一个编译器错误:http://ideone.com/jriyay
prog.cpp: In function 'int main()':
prog.cpp:21:14: error: cannot declare variable 'w' to be of abstract type 'Word'
Word w;
prog.cpp:11:12: note: because the following virtual functions are pure within 'Word':
struct Word : public IAudible {
^
prog.cpp:4:22: note: virtual void IAudible::sound() const
virtual void sound() const = 0;
C++ 还没有 Concepts :-( 但 gcc-6 实现了它:
template <class T>
concept bool Animal() {
return requires(const T& a) {
{a.sound()} -> std::string;
};
}
void listenTo(const Animal& animal) {
std::cout << animal.sound() << std::endl;
}
但是您可以使用
typename <typename T>
using sound_type = decltype(std::declval<const T&>().sound());
template <typename T>
using has_sound = is_detected<sound_type, T>;
template <typename T>
using is_animal = has_sound<T>;
// or std::conditional_t<has_sound<T>::value /*&& other_conditions*/,
// std::true_type, std::false_type>;
然后是常规 SFINAE:
template<class T>
std::enable_if_t<is_animal<T>::value>
listenTo(const T& animal) {
std::cout << animal.sound() << std::endl;
}