【问题标题】:Pass function call from a class to one of its members based on another member parameter根据另一个成员参数将函数调用从一个类传递给它的一个成员
【发布时间】:2020-06-10 05:42:04
【问题描述】:

假设我有以下class A,它通过不同的函数调用和包装器传递。

class A{
std::vector<int> a;
public:
int getSize const {return a.size();}
int getVal(int i) const {return a[i];}
// other private and public members and functions 
}

现在出于某种原因,我需要相同的类,但使用双向量。我无法对此类进行模板化,因为有许多我无法更改的函数签名。建议将A 重命名为A0,将其模板化,创建包含A0&lt;int&gt;A0&lt;double&gt; 的新A,如下所示:

template <typename T>
class A0{
std::vector<T> a;
public:
int getSize const {return a.size();}
T getVal(int i) const {return a[i];}
// other private and public members and functions 
}

class A{
// only one of the following will be initialized in the constructor and the other one will be null.
std::shared_ptr<A0<int>> iA;
std::shared_ptr<A0<double>> dA;
// also the following flag will be set in the constructor
bool isInt;
}

这是一个问题:如果我想在之前访问、更改或刚刚传递类A 的实例的代码的不同位置进行最小的更改,应该怎么做?例如,在旧代码的不同部分考虑这一点:

A x;
int n = x.getSize();

有没有办法保留旧代码,而无需在新的 A 类中实现方法 getSize(),该类将包含 if 条件语句并基于 isInt 返回 iA-&gt;getSize()dA-&gt;getSize()?有没有聪明的方法来做到这一点?

对于在使用(主要是绕过)旧A的代码的不同部分实现最小修改的目标,是否有任何其他建议?

}

【问题讨论】:

  • 您期望什么类型,例如getVal 在不破坏之前依赖它的代码的情况下返回 int?
  • 当然这不是真正的代码,但是在这种地方,它会针对每种情况进行专门处理。大多数情况下,此类仅从一种方法传递到另一种方法,直到它到达完成实际操作的方法(并且可以针对那里的每种情况处理事情)

标签: c++ class templates call function-call


【解决方案1】:

如果这只是关于传递对象(或对它的引用)而不是实际使用对象的任何成员,那么您可以简单地使用std::variant

using A = std::variant<A0<int>, A0<double>>;

在实际使用成员的地方,使用std::visit 确定类型并对其进行操作。


如果仅通过引用传递,则将A 设为A0&lt;T&gt; 的空基类也可以。然后,您可以使用static_cast&lt;A0&lt;int&gt;&amp;&gt;(...)static_cast&lt;A0&lt;double&gt;&amp;&gt;(...) 转换回真实类型,以使用目的地的成员。但是,您必须确保转换为所传递对象的实际类型,否则您将出现未定义的行为。

另一种选择是dynamic_cast,而不是static_cast,如果类型不匹配,它将抛出或返回一个空指针。但这需要基类A 有一个多态的虚方法。

【讨论】:

  • 是否可以将 A 作为 A0 的基类,而 A0 在 A 内部使用?
  • @KuroshMotamedian 这里的“used”是什么意思?不清楚你的问题是针对什么的。
  • 我的意思是 A 的成员是指向 A0 类型的共享指针。以下可能吗?class A{ B b; //... };类 B:public A{ //... };
  • @KuroshMotamedian 在我的两个建议中,A 都没有任何用户指定的成员。你把AA0 弄混了吗?我的第二个建议假设A 是一些空基,因此派生的A0&lt;T&gt; 可以作为对A 的引用传递,但只能在static_cast 回到A0&lt;T&gt; 之后使用。然而,这确实(与第一个解决方案相反),目标先验地知道传递的A&amp; 是否真的引用A0&lt;int&gt;A0&lt;double&gt;。如果你投错了,你就会有UB。另一种选择是dynamic_cast,但A 必须是多态的,即具有虚拟函数
  • @KuroshMotamedian 请参阅我的答案中的更新以了解重要细节。
【解决方案2】:

std::variant 可能是您需要的。创建一个buffer 类并将所有容器操作转发给它。你的班级A 没有必要改变太多,但你应该在buffer 中模仿std::vector&lt;T&gt;,我实现了size()const subscript operator。这是基本的演示。

#include <variant>
#include <vector>
#include <iostream>

struct store_as_int {};
struct store_as_double {};

class buffer {
    public:
        buffer( store_as_int ) : data { std::vector<int>{} } {
            std::cout << "I am storing numbers as int" << std::endl;
        }
        buffer( store_as_double ) : data { std::vector<double>{} } {
            std::cout << "I am storing numbers as double" << std::endl;
        }

        [[nodiscard]] std::size_t size() const noexcept {
            std::size_t s;
            std::visit( [ &s ]( auto&& arg ) {
               s = arg.size(); 
            } , data );

            return s;
        }

        [[nodiscard]] double operator[]( std::size_t idx ) const {
            double s;
            std::visit( [ &s , idx ]( auto&& arg ) {
               s = arg[ idx ]; 
            } , data );

            return s;
        }

    private:
        std::variant< std::vector<int> , std::vector<double> > data;
};

class A{
    buffer a;
public:
    A() : a { store_as_int{} } {}
    A( store_as_double ) : a { store_as_double {} } {   }
    int getSize() const { return a.size(); }
    int getVal(int i) const { return a[i]; }
};

int main()
{
    A x;
    A y { store_as_double{} };
    int n = x.getSize();
    int t = x.getSize();

    std::cout << n << std::endl;
    std::cout << t << std::endl;
}

输出:

我将数字存储为 int
我将数字存储为 double
0
0

run online

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 2016-09-11
    • 1970-01-01
    • 2014-02-20
    相关资源
    最近更新 更多