SFINAE 的概念

问题描述 投票:0回答:2

我有一个抽象基类,然后是一个模板化派生类,对于某些类型(由模板指定),我想在派生类中实现虚拟基方法,对于其他类型,这应该保持开放状态(需要进一步推导):

#include <iostream>

struct cat {
void meow() {
        std::cout << "Meow!\n";
    }
};

struct base {
    virtual void foo_base() = 0;
};

template<typename T>
concept can_meow = requires(T t){t.meow();};

template<class T>
struct derived: public base {
derived() requires can_meow<T> {
    std::cout << "Wrapper constructor with meow!\n";
}

derived() requires (!can_meow<T>) {
    std::cout << "Wrapper constructor w/o meow!\n";
}

void foo_base() requires can_meow<T> final {};
};

template<class T>
struct derived2: public derived<T> {
    void foo_base() final {};
};

int main() {
    derived<cat> c; //works
    //derived2<int> i; //error
    return 0;
}

如何让它发挥作用?我目前遇到的错误是:

<source>:28:6: error: use of function 'void wrapper<T>::foo_base() requires  can_meow<T> [with T = int]' with unsatisfied constraints

干杯和感谢

更新:godbolt https://godbolt.org/z/T4v7Yjq5f

c++ c++20 sfinae c++-concepts
2个回答
4
投票

不允许以任何方式约束虚函数(请参阅:[class.virtual]p6)。

您可以通过使用可以重写该函数的条件基类来实现类似的效果。例如:

namespace detail {
    template<class T>
    struct derived_impl : base {
        using base::base;
    };

    template<can_meow T>
    struct derived_impl<T> : base {
        using base::base;

        void foo_base() final {}
    };
}

template<class T>
struct derived: detail::derived_impl<T> {
    derived() requires can_meow<T> {
        std::cout << "Wrapper constructor with meow!\n";
    }

    derived() requires (!can_meow<T>) {
        std::cout << "Wrapper constructor w/o meow!\n";
    }
};

0
投票

正如 @artyer 已经指出的那样,您不能以这种方式限制虚拟函数。引入条件中间类的技术是一种很好的技术,但是您可以使用一些 CRTP 技巧来减少侵入性。

template<class D, class B, bool route_foo>
struct foo_router:B {
  using B::B;
};
template<class D, class B>
struct foo_router<D,B, true>:B {
  using B::B;
  void foo_base() final {
    return static_cast<D*>(this)->foo_base_impl();
  }
};

然后我们做:

template<class T>
struct derived: public foo_router<derived<T>, base, can_meow<T>> {
  derived() requires can_meow<T> {
    std::cout << "Wrapper constructor with meow!\n";
  }

  derived() requires (!can_meow<T>) {
    std::cout << "Wrapper constructor w/o meow!\n";
  }

  void foo_base_impl() requires can_meow<T> /*final*/ {};
};

template<class T>
struct derived2: public derived<T> {
  void foo_base() final {};
};

foo_router
根据
foo_base
有条件地将
foo_base_impl
路由到
can_meow<T>
。在此实现中,它无论如何都存在,但如果
!can_meow<T>
.

则不执行任何操作

实时示例 - 更改仅限于重命名和从

final
中剥离
foo_base_impl
、额外的
router
类以及从
derived
的继承。

© www.soinside.com 2019 - 2024. All rights reserved.