SFINAE отключить конструктор, если существует преобразование из `double` в` T` - PullRequest
1 голос
/ 26 января 2020

Ниже приводится минимальная значимая программа, которую я мог бы придумать, чтобы воспроизвести мое затруднительное положение для этого вопроса. Программа не может быть скомпилирована из-за конфликта между конструкторами LinearForm<double>. Чтобы разрешить этот конфликт, я хотел бы включить LinearForm<V>::LinearForm(double) тогда и только тогда, когда не существует преобразования из double в V. Как я могу это сделать? (И разрешит ли это конфликт между конструкторами?)

#include <type_traits>
#include <array>

template<int N>
struct Vector{
    std::array<double,N> coords;

    Vector(std::array<double,N> coords) : coords(coords) {}

    // implicit conversions between scalar and Vector<1>
    template<int Nd = N, std::enable_if_t<Nd==1>>
    Vector(double scalar) : coords(scalar) {}
    template<int Nd = N, std::enable_if_t<Nd==1>>
    operator double() const {return coords[0];}

    double dot(Vector<N> u) const {
        double acc = 0;
        for(int i=0; i<N; i++){
            acc += coords[i]*u.coords[i];
        }
        return acc;
    }

    static Vector<N> zero(){ return Vector<N>(std::array<double,N>{}); }
};

template<typename V> // V is domain element type i.e. LinearForm maps from V to double
struct LinearForm {
    V v;
    LinearForm(V v) : v(v) {}

    //template<typename Vd=V, typename = std::enable_if_t</* WHAT TO PUT IN HERE */>>
    LinearForm(double v) : LinearForm(V::zero())
    {
        if(v != 0){
            throw std::runtime_error("LinearForm cannot be non-zero constant.");
        }
    }
    double operator()(V u){return u.dot(v);}
};

int main()
{
    LinearForm<Vector<2>> lf(Vector<2>({1,2}));
    LinearForm<Vector<2>> zf = 0;

    LinearForm<double> slf = 0;

    auto u = Vector<2>({3,4});
    lf(u); // returns some value
    zf(u); // should return zero for any u

    return 0;
}

Ответы [ 2 ]

2 голосов
/ 26 января 2020

Вы можете использовать std::is_convertible. Затем это сделает работу:

template <typename Vd=V,
          typename std::enable_if_t<std::is_convertible<double, Vd>::value>::type* = nullptr>
LinearForm(double v) : LinearForm(V::zero())
{
    if(v != 0){
        throw std::runtime_error("LinearForm cannot be non-zero constant.");
    }
}

Чтобы ваш код компилировался, мне нужно было добавить еще две вещи:

LinearForm(int value) : v(value) {}

и изменить

template<int Nd = N, std::enable_if_t<Nd==1>>
Vector(double scalar) : coords(scalar) {}

до

template<int Nd = N>
Vector(double scalar) : coords({scalar}) {}

Живой пример

1 голос
/ 26 января 2020
std::enable_if_t<std::is_convertible_v<double, Ty>>* = nullptr>

- это то, что вы ищете

...