C++ 在接口描述中使用=删除

C++ 在接口描述中使用=删除,c++,c++11,sfinae,C++,C++11,Sfinae,我试图为自由函数listenTo(SomeAnimal)提供一个接口描述,该函数应该在满足特定类型要求的类型上运行(它应该是动物)。函数参数不应使用纯虚方法的接口继承机制 我破解了一个解决方案,其中free函数通过sfinae语句检查参数类型以查找基类。为了保证参数实现基类的接口,我使用=delete删除了基类方法。我没有在互联网上找到任何类似的解决方案,因此,我不确定它是否有意义,但它是有效的 给你,有什么意见吗 #include <iostream> #include <t

我试图为自由函数
listenTo(SomeAnimal)
提供一个接口描述,该函数应该在满足特定类型要求的类型上运行(它应该是动物)。函数参数不应使用纯虚方法的接口继承机制

我破解了一个解决方案,其中free函数通过sfinae语句检查参数类型以查找基类。为了保证参数实现基类的接口,我使用
=delete
删除了基类方法。我没有在互联网上找到任何类似的解决方案,因此,我不确定它是否有意义,但它是有效的

给你,有什么意见吗

#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;
}
#包括
#包括
类动物{
公众:
//需要实现的接口
std::string sound()const=delete;
受保护的:
IAnimal(){}
};
类别类别:公共动物{
公众:
//实现删除的方法
std::string sound()常量{
返回标准::字符串(“喵喵”);
}
};
类通配符:公共猫{
公众:
//覆盖猫的声音方法
std::string sound()常量{
返回标准::字符串(“Rarr”);
}
};
犬类:公共动物{
公众:
//实现删除的方法
std::string sound()常量{
返回std::string(“Wuff”);
}
};
班车{
公众:
//实现删除的方法
std::string sound()常量{
返回std::字符串(“Brum”);
}
};
//Sfinae正确继承测试
模板
void listenTo(塔尼马尔常量和a){

另一种避免继承复杂性的方法是创建类型特征:

#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;
}
#包括
#包括
模板
结构是_animal:std::false _type{};
班猫{
公众:
std::string sound()常量{
返回标准::字符串(“喵喵”);
}
};
模板结构是_animal:std::true_type{};
类通配符:公共猫{
公众:
//覆盖猫的声音方法
std::string sound()常量{
返回标准::字符串(“Rarr”);
}
};
模板结构是_animal:std::true_type{};
班犬{
公众:
std::string sound()常量{
返回std::string(“Wuff”);
}
};
模板结构是_animal:std::true_type{};
班车{
公众:
std::string sound()常量{
返回std::字符串(“Brum”);
}
};
//Sfinae正确继承测试
模板
void listenTo(塔尼马尔常量和a){
标准::cout
当且仅当
t.sound()
对调用有效时,
can\u 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;
}
现在我们可以升级我们的动物试验:

template<class T>
using is_animal = bool_t< can_stream_sound<T>{} >;
模板
使用is_animal=bool_t
您没有要求其他解决方案。相反,您要求对您的解决方案发表意见。
好吧,这是我的意见,希望能对你有所帮助


这是一个较弱的sfinae表达式。您可以使用以下方法轻松将其破坏:

listenTo<Car, void>(car);
通过这种方式,您还可以从
IAnimal
中删除无用的
sound
定义,但仍然会有一个很好的编译错误


现在,如果您还想删除
IAnimal
接口,可能的解决方案(其他答案中没有提到)如下:

或更紧凑的版本:

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; }
};
模板
结构检查:检查{
静态_断言(decltype(std::declval().sound(),std::true_type{}){},“!”;
使用check::verify;
静态constexpr bool验证(标记){return true;}
};
这是一种仅使用当前版本语言的功能检查概念的方法。

请注意,在代码中的某些地方,概念也会有助于实现同样的功能,但它们还不是标准的一部分。

delete
如果函数删除它,它不会引入对它的依赖关系。它说“这个类没有这个函数”就实现/注释界面而言,这是实现目标的一种奇怪的方式。这有点像构建一个完整的驾驶舱F-32模拟器,然后告诉一个非常困惑的第一个试飞员“我们移除了所有的按钮,这样你就会知道真实飞机上到底存在着什么”

接口是用C++实现的,它是用虚函数来注释的,你把一个虚函数注解为“纯”(要实现),给它一个“0”的体,比如:

struct IFace {
    virtual void sound() = 0;
};
这使得不可能创建
IFace
的具体实例或从中派生的任何类,直到您到达实现
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);
}
C++还没有:-(但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;
}
然后是常规SFINAE:

template<class T>
std::enable_if_t<is_animal<T>::value>
listenTo(const T& animal) {
    std::cout << animal.sound() << std::endl;
}
模板
std::如果启用,则启用
listenTo(施工和动物){

Std::CUT,你总是可以省略定义(而不是<代码>删除;< /COD> >是的,你可以,但是你得到了一个不太好的<代码>未定义的引用,从编译器(在你忽略接口的情况下)。我在Stroustup的书(C++编程语言2013)中想到。他用一个类似的例子描述了这种方法。我觉得它不错,但我几乎没有资格评估它。出于兴趣,如果
sound()
不是多态的,为什么还要使用继承呢?您可能正在寻找
virtual std::string sound()const=0
?来使函数“纯虚拟”吗因此,强制派生类提供实现?你是对的,这有点奇怪。我知道纯虚拟方法允许强制实现这些方法,但正如我提到的,我不想使用纯虚拟方法(例如,由于性能原因)@erikzenker我在一些相当大规模的系统上工作过,做了很多性能方面的工作,我还在等待我说“啊哈!这是一个虚拟函数调用!”好吧,除非你使用的是90年代中期的硬件和匹配的编译器。你提供的链接清楚地表明:虚拟方法对性能有影响,特别是当它们经常被调用时。谢谢你的帮助
template<class TAnimal>
void listenTo(TAnimal const & a ) {
    static_assert(std::is_base_of<IAnimal, TAnimal>::value, "!");
    std::cout << a.sound() << std::endl;
}
#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;
}
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; }
};
struct IFace {
    virtual void sound() = 0;
};
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);
}
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;
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>;
template<class T>
std::enable_if_t<is_animal<T>::value>
listenTo(const T& animal) {
    std::cout << animal.sound() << std::endl;
}