【问题标题】:auto type deduction coercion for templated class?模板类的自动类型扣除强制?
【发布时间】:2017-08-06 01:21:08
【问题描述】:

我正在构建的模板类中有 2 个问题。我在下面包含了示例代码。第一个问题是我是否可以强制为模板类扣除自动类型。即:

自动 p = myvar;

myvar 在哪里是 T<...>,我可以强制自动检测 Q<...> 吗?这是简化的。继续阅读以获得更清晰的解释。

为清楚起见进行了编辑:让我解释一下我在做什么。而且我还想指出,这种风格的代码在一个大型项目上运行得非常好。我正在尝试添加一些特性和功能,并消除一些更尴尬的行为。

代码使用模板对 n 维数组执行工作。模板有一个顶层类,下面有一个存储类。将存储类传递到顶级类允许继承存储类的顶级类。所以我从 NDimVar 开始,我有 NDimStor。我最终得到了

NDimVar<NDimStor>

除了数据缓冲区之外,该类不包含任何数据:

class NDimStor<size_t... dimensions> {
int buffer[Size<dimensions...>()]
}

这使得 1234562 的地址 == 缓冲区的地址。这是整个实施的关键。这是一个不正确的假设吗? (我可以看到这在我的系统上运行没有任何问题,但也许并非总是如此。)

当我创建 NDimVar> 时,我最终得到了一个 10x10 数组。

我有获取数组片段的函数,例如:

NDimVar<NDimStor<dimensions...>>::RemoveDim & get(int index);

这会从二维 10x10 数组中创建一个包含 10 个元素的新一维数组:

NDimVar<NdimStor<10>>

为了将其作为参考返回,我在所需数据的位置使用了 reinterpret_cast。所以在这个例子中,get(3) 会执行:

return reinterpret_cast<NDimVar≤NDimStor<dimensions...>>::RemoveDim&>(buffer[index * DimensionSumBelow<0>()]);

DimensionSumBelow 返回维度为 1+ 的元素之和,即 10。因此 &buffer[30] 是引用的 1d NDimVar 的地址。

所有这些都非常有效。

我唯一的问题是我想添加叠加层。例如,能够返回对新类的引用:

NDimVar<NDimPermute<NDimStor<10,10>,1,0>>

指向相同的原始位置以及置换行为(交换维度)。这也很有效。但我想要:

auto p = myvar.Permute<1,0>()

使用置换数据创建 myvar 的新副本。如果我说,这将起作用:

NDimVar<NDimStor<10,10>> p = myvar.Permute<1,0>().

我觉得我可以做一些自动类型推断的东西来强制返回自动类型,但我不确定。我一直想不通。

再次感谢, 那胡姆

我想要的是: 1. 在我的存储上创建临时覆盖类,例如A_top 可以在不创建新对象的情况下返回一个名为 A_top> 的类型,它只返回对该类型的引用。这改变了访问存储的方式。问题出在调用 auto 时。我不希望直接实例化这种类型。是否可以将返回的 auto 修改为原始的 A_top?

#include <iostream>
using namespace std;

class A_storage {
    public:
    float arr[10];
    A_storage () {
    }
    float & el (int index) {
        return arr[index];
    }
};

template <typename T> class A_overlay : T {
    private:
    A_overlay () {
        cout << "A_overlay  ()" << endl;
    }
    A_overlay (const A_overlay  &) {
        cout << "A_overlay  (&)" << endl;
    }
    public:
    using T::arr;
    float & el (int index) {
        return arr[10 - index];
    }
};

template <typename T> class A_top;

template <typename T> class A_top : public T {
    public:
        A_top () {
        }
        A_top<A_overlay<A_storage>> & get () {
            return reinterpret_cast<A_top<A_overlay<A_storage>>&>(*this);
        }
};

using A = A_top<A_storage>;

int main (void) {
    A a;
    auto c = a.get(); // illegal - can i auto type deduce to A_top<A_storage>?
    return 0;
}
  1. 如果函数接受 (A_top &) 作为参数,如何创建可以将 A_top>& 转换为 A_top& 的转换函数?

谢谢, 那胡姆

【问题讨论】:

  • 如果 auto 让您感到困惑,请从不使用它开始。输入你真正想要的类型,让编译器告诉你是否有问题。但是,鉴于您对 C++ 的基本部分缺乏了解,您正在做的事情可能太复杂了。我建议从更简单的开始。您似乎有多层困惑,这将使您难以理解对您的特定问题的任何答案 - 并且也使您的问题难以回答,因为它有些荒谬。

标签: c++ templates


【解决方案1】:

首先,您的设计对我来说看起来不正确,我不确定该行为是否实际上定义明确。 (可能不会。)

无论如何,问题不在于auto。该错误是由于A_overlay的复制构造函数是私有的,而您需要将a.get()返回的A_top&lt;A_overlay&lt;A_storage&gt;&gt;复制到auto c

(请注意,在这种情况下,auto 显然会被推断为 A_top&lt;A_overlay&lt;A_storage&gt;&gt;,我假设您在说它是 A_top&lt;A_storage&gt; 时打错了。)


另请注意,A_top::get() 中的 A_storage 应替换为 T,即使它不会更改您的 sn-p 中的任何内容,因为您只有 T == A_storage


如果一个函数接受 (A_top &) 作为参数,我如何创建一个可以将 A_top> 转换为 A_top& 的转换函数?

嗯,不就是这样吗:

return reinterpret_cast<A_top<A_storage>&>(obj);

【讨论】:

  • @nachum 这对我来说没有意义。你可以让 get() 返回 A_top&lt;A_storage&gt; ,但是get() 有什么意义呢?
  • 代码正确。我想要返回覆盖版本。但我不希望在分配时创建 A_overlay。这就是为什么它有一个私有的拷贝构造函数。那是故意的。我希望自动返回 A_top,即使 get() 返回 A_top> 的引用。有没有办法操纵自动类型检测来获得这种行为? ——
  • @nachum 但它们不可兑换。即使A_top&lt;A_storage&gt; c = a.get() 也行不通。看起来你需要使用多态性。
  • 这很好用。我有一个将数据复制到新对象的转换构造函数。你知道auto可以强制吗?这对我来说是最有趣的。
  • @nachum 那么为什么你没有在问题中显示该构造函数?不,那将违背auto 的目的。为什么不将.get() 的返回类型更改为A_top&lt;A_storage&gt;“我希望返回覆盖版本。” 不是有效答案。
【解决方案2】:

reinterpret_cast 几乎不应该被使用。它基本上删除了类型相关的任何编译器验证。并且进行不相关的强制转换本质上是未定义的行为,因为它本质上假设派生类始终位于偏移量 0...

编写这样的代码没有任何意义。它是不可维护的,也很难理解你想要实现的目标。看起来您想假装您的 A_top&lt;A_storage&gt; 对象是 A_top&lt;A_overlay&lt;A_storage&gt;&gt; 对象。 如果这是您想要做的,则将 A 别名声明为该类型。

在您的代码中,您似乎想要反转索引,以便在您询问位置 0 的项目时返回位置 10 的项目,反之亦然。 你真的认为,从你的混淆代码中可以看出这一点吗?永远不要写这么糟糕的代码。

有点像

class A_overlay {
public:
    float & el (int index) { return arr[10 - index]; }

private:
    A_storage arr;
};

会比您当前的代码更有意义。

  • 不需要演员表。
  • 易于理解。
  • 定义明确的行为。
  • 你可以保住你的工作。

显然,您将根据需要更新以下行:

using A = A_top<A_storage>;

另外,如果A_top没有用处,那为什么不直接使用A_overlay呢?如果A_storage 不是模板,为什么还要使用模板?你真的想在你的代码库的其他地方重用这些乱七八糟的东西吗?

很明显,如果你写这样的代码,你的代码继承不尊重IS-A关系。所以这显然是一个糟糕的设计!

【讨论】:

  • 请看我上面添加的描述。叠加层是动态添加的,以允许排列排列或排列类的部分切片。所以我不能为所有可能创建的模板设置别名。我的例子太简单了,对不起!看看这个问题现在是否更清楚了。任何和所有的建议表示赞赏。谢谢!
  • 返回代理而不是假装您的数据是另一种类型。据我所知,您通过假设数据具有给定的布局来依赖未定义的行为。如果添加了虚函数或使用了多重继承,这很容易破坏......
  • 但是代理是一个新类,我希望引用我的数据作为参考。为用户保留最简单的 C++ 语义。此外,派生类应该可以正常工作。是什么让你认为它不会?
  • 我已经解释了为什么我认为你正在做的事情很糟糕......从本质上讲,它使代码变得不那么清晰、不那么可移植和不那么可维护。代理解决方案不再添加类而不是强制转换,但它是一种标准方式,而不是滥用系统。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-19
  • 2012-10-30
  • 1970-01-01
  • 1970-01-01
  • 2015-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多