由于继承,operator <<()无法从模板解析

时间:2019-06-18 19:51:51

标签: c++ templates compiler-errors operator-overloading c++17

我正在使用一组类,我的主要代码如下:

main.cpp

#include "calc.h"

int main() {
    neg_inf nif;
    pos_inf pif;

    limit<double, infinity> l( 3.4, nif, pif, 2.2 )

    std::cout << "value dx  = " << l.value() << '\n'
              << "lower lim = " << l.lower() << '\n'
              << "upper lim = " << l.upper() << '\n'
              << "step_size = " << l.step() << '\n';

    return EXIT_SUCCESS;
}

预期输出应为:

value dx  = 3.4
lower lim = -inf
upper lim = inf
step_size = 2.2

这是我的课程:

计算小时

#pragma once

#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits> 

struct infinity {
protected:
    infinity() = default;
};

struct pos_inf : public infinity {
    constexpr double operator()() { return std::numeric_limits<double>::infinity(); }
};

struct neg_inf : public infinity {
   constexpr double operator()() { return -std::numeric_limits<double>::infinity(); }
};

std::ostream& operator<<( std::ostream& os, const pos_inf& inf );
std::ostream& operator<<( std::ostream& os, const neg_inf& inf );

template<typename dX, class bound>
class limit {
    dX dx;
    bound lowerBound;
    bound upperBound;
    double step_size;

public:
    limit( dX x, bound lower, bound upper, double step = 1 ) :
        dx{ x }, lowerBound{ lower }, upperBound { upper }, step_size { step }
    {}

    dX value() const { return dx; }
    bound lower() const { return lowerBound; }
    bound upper() const { return upperBound; }
    double step() const { return step_size; }
};

calc.cpp

#include "calc.h"

std::ostream& operator<<( std::ostream& os, const pos_inf& inf ) {
    // originally intended to do:
    // return os << inf(); // but fails to compile

    auto v = pos_inf()(); // this works
    return os << v;
}

std::ostream& operator<<( std::ostream& os, const neg_inf& inf ) {
    // same as above...

    auto v = neg_inf()();
    return os << v;
}

但是在main.cpp中,Visual Studio 2017会生成此编译器错误:

c:\***\main.cpp(33): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'bound' (or there is no acceptable conversion)
1>        with
1>        [
1>            bound=infinity
1>        ]

基于以下代码行:

<< "lower lim = " << l.lower() << '\n'

并且从l.lower()失败

但是,如果我在main中这样做:

#include "calc.h"

int main() {
    neg_inf nif;
    pos_inf pif;

    std::cout << nif << '\n' << pif << '\n'

    return EXIT_SUCCESS;
}

我得到了输出:

-inf
inf

这告诉我我的operator<<()正在为继承的结构工作,但是当我将其父类型作为模板参数传递并将派生类型传递到我的limit类的构造函数中时,{ {1}}没有解决。这似乎是一个模棱两可的问题,但我不确定如何解决。我在这里想念或忽略了什么?


作为该问题之外的补充说明,有没有一种更优雅的表示operator<<()的方式?我在这里使用继承是因为-/+inf+ -不是数字,而是一个概念,它们彼此相似,但指向不同的方向。因此,当我将无穷大类型作为模板参数传递时,我希望能够将下限设置为-inf,将上限设置为+ inf。我希望绑定类型是模板,因为我可能想在inf[-1,1]之间使用整数边界或双精度边界,其中这些都是数字边界。我不确定如何以更优雅的方式表达无限,任何技巧或建议都将不胜感激。

4 个答案:

答案 0 :(得分:2)

好吧,您为dictionary = {} with open("en-dict.txt", "r", encoding="utf8") as file: for line in file: line = file.readline().strip() dictionary[line] = 0 if "eat" in dictionary: print("Yes") 使用了operator<<const pos_inf& inf进行了重载,但是您使用const neg_inf& inf作为模板类型,因此使用了infinity方法返回lower()。当然,将不会使用您的运算符重载,因为它们是从infinity派生的类型。为什么不让infinity的{​​{1}}重载呢?

一些快速的解决方法:

  1. 使operator<<虚拟。但是您不能将其与infinity混合使用。
  2. 使用double operator()()类的constexpr实际上指定两个边界的类型,然后您的template<typename dX, class lower_bound, class upper_bound>limits方法可以返回lower和{{ 1}}类型,您当前的运算符将起作用。另外,为简单起见,如果类型不一定总是不同,也可以将第一种默认为第二种-upper
  3. 在详细介绍了设计之后-为什么不真正将pos_inf类作为模板(因为我假设您希望它与neg_inf匹配并在那里实现限制? >

    template<typename dX, class lower_bound,  class upper_bound = lower_bound>
  4. 使infinity返回dX。这样,您实际上将分辨率从绑定类型保留为模板中所需的值类型,并且可以混合使用无限和非无限限制。

    #include <cmath>
    #include <iostream>
    #include <limits>
    #include <type_traits> 
    
    template<typename T>
    struct infinity {
    public:
        infinity() = default;
    
        constexpr double lower()
        {
            return -std::numeric_limits<T>::infinity();
        }
    
        constexpr double upper()
        {
            return std::numeric_limits<T>::infinity();
        }
    };
    
    
    template<typename dX>
    class limit {
        dX dx;
        double step_size;
    
    public:
        limit(dX x, double step = 1) :
            dx{ x }, step_size{ step }
        {}
    
        dX value() const { return dx; }
        dX lower() const { return infinity<dX>().lower(); }
        dX upper() const { return infinity<dX>().upper(); }
        double step() const { return step_size; }
    };
    
    
    int main() {
    
        limit<double> l(3.4, 2.2);
    
            std::cout << "value dx  = " << l.value() << '\n'
            << "lower lim = " << l.lower() << '\n'
            << "upper lim = " << l.upper() << '\n'
            << "step_size = " << l.step() << '\n';
    
        return EXIT_SUCCESS;
    }
    

答案 1 :(得分:2)

不要以这种方式为子类重载运算符。使用虚拟方法进行输出,并将泛型与调用虚拟方法的重载运算符一起使用:

class infinity {
  public:
    virtual ostream &printTo(ostream &o) const = 0;
};
ostream &operator<<(ostream &o,const infinity &i) {
  return i.printTo(o);
}
class neg_inf : public infinity {
  public:
    virtual ostream &printTo(ostream &o) const {
        // do what you want
        return o;
    }
};

答案 2 :(得分:1)

我认为您对自己的约束太多了:您可以删除基类,为operator<<pos_inf添加neg_inf并向limit添加一个额外的类型,这样,您可以拥有不同类型的两个边界。这是我的意思:

Calc.h

#pragma once

#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits> 


struct pos_inf {
    constexpr double operator()() const { return std::numeric_limits<double>::infinity(); }
};

struct neg_inf  {
    constexpr double operator()() const { return -std::numeric_limits<double>::infinity(); }
};
// Both operators defined
std::ostream& operator<<(std::ostream& os, const pos_inf& inf);
std::ostream& operator<<(std::ostream& os, const neg_inf& inf);

//extra template type  in limit
template<typename dX, class lowerBoundType, class UpperBoundType>
class limit {
    dX dx;
    lowerBoundType lowerBound;
    UpperBoundType upperBound;
    double step_size;

public:
    limit(dX x, lowerBoundType lower, UpperBoundType upper, double step = 1) :
        dx{ x }, lowerBound{ lower }, upperBound{ upper }, step_size{ step }
    {}

    dX value() const { return dx; }
    lowerBoundType lower() const { return lowerBound; }
    UpperBoundType upper() const { return upperBound; }
    double step() const { return step_size; }
};

Calc.cpp

#include "calc.h"

std::ostream& operator<<(std::ostream& os, const pos_inf& inf) {
    return os << inf(); // but fails to compile

}

std::ostream& operator<<(std::ostream& os, const neg_inf& inf) {
    return os << inf(); // but fails to compile

}

main.cpp

#include "calc.h"

int main() {
    neg_inf nif;
    pos_inf pif;

    limit<double, neg_inf, pos_inf> l(3.4, nif, pif, 2.2);

        std::cout << "value dx  = " << l.value() << '\n';
        std::cout << "lower lim = " << l.lower() << '\n';
        std::cout << "upper lim = " << l.upper() << '\n';
        std::cout << "step_size = " << l.step() << '\n';

    return EXIT_SUCCESS;
}

如果这不是您想要的,我表示歉意。

答案 3 :(得分:0)

在考虑了一些留下答案的人和其他留下评论的人的反馈,并考虑到上下限可能不是同一类型之后,我添加了额外的template参数。在这个特定的实现中,这是不可避免的。但是通过这样做,我完全可以消除对继承的需要,并且只为每种类型创建了两个不同的结构。这也简化了我的operator<<()。所以我的课程现在看起来像这样:

计算小时

#pragma once

#include <cmath>
#include <iostream>
#include <limits>
#include <type_traits>

struct neg_inf {
    constexpr double operator()() { return -std::numeric_limits<double>::infinity(); }
};

struct pos_inf {
    constexpr double operator()() { return std::numeric_limits<double>::infinity(); }
};

template<typename dX, class LowerBound, class UpperBound>
class limit {
    dX dx;
    LowerBound lowerBound;
    UpperBound upperBound;
    double step_size;

public:
    limit( dX x, LowerBound lower, UpperBound upper, double step = 1 ) :
        dx{ x }, lowerBound{ lower }, upperBound { upper }, step_size{ step }
    {}

    dX value() const { return dx; }
    LowerBound lower() const { return lowerBound; }
    UpperBound upper() const { return upperBound; }
    double step() const { return step_size; }
};

calc.cpp

#include "calc.h"

std::ostream& operator<<(std::ostream& os, const neg_inf& inf) {
    // not using the parameter, using constructor and its operator()
    // since this is a function object or functor and returns a constexpr
    return os << neg_inf()();
}

std::ostream& operator<<(std::ostream& os, const pos_inf& inf) {
    // not using the parameter, using constructor and its operator()
    // since this is a function object or functor and returns a constexpr
    return os << pos_inf()();
}

现在主要,与我的原始作品非常相似,但有一些修改:

#include "calc.h"

int main() { 
    neg_inf nif;
    pos_inf pif;

    limit<double, neg_inf, pos_inf> l(3.4, nif, pif, 2.2);

    std::cout << "value dx  = " << l.value() << '\n'
              << "lower lim = " << l.lower() << '\n'
              << "upper lim = " << l.upper() << '\n'
              << "step_size = " << l.step() << '\n';

    return EXIT_SUCCESS;
}

这确实有效,并给了我输出:

value dx  = 3.4
lower lim = -inf
upper lim = inf
step_size = 2.2

注意,但是在考虑了这一点并将其发挥作用并将其与确实匹配的其他一些答案进行比较之后,用户的curiouslyrecurringthoughts答案。