首页 \ 问答 \ C ++中转换函数模板参数推导的含义(Implications of conversion function template argument deduction in C++)

C ++中转换函数模板参数推导的含义(Implications of conversion function template argument deduction in C++)

我无法理解C ++标准中转换函数模板参数推导规则的含义。 该标准规定([temp.deduct.conv]第1节,N4594中的第14.8.2.3.1节):

通过将转换函数模板(称为P)的返回类型与作为转换结果所需的类型(称为A;参见8.5,13.3.1.5和13.3.1.6确定该类型)如14.8.2.5所述。

其中14.8.2.5([temp.deduct.type])是描述通用模板参数演绎的部分(尽管最常见的情况是函数调用模板参数演绎[temp.deduct.call])似乎不再指向那里;它曾经?)。 然而,下一个条款让我感到困惑(第2条):

如果P是一个引用类型,则用P引用的类型代替P,用于类型推导以及本节其余部分对P的进一步引用或转换。

对我来说,这似乎意味着template <class T> operator T()template <class T> operator T&()是相同的(并且指定两者都会导致不明确性)。 但是在我使用过的任何编译器中情况并非如此! 例如:

struct any1 { template <typename T> operator T() { } };

struct any2 { template <typename T> operator T&() { } };

void f1(int) { }
void f2(int&) { }
void f3(int const&) { }

int main() {
  f1(any1());
  // f2(any1()); compile time error
  f3(any1());

  f1(any2());
  f2(any2());
  f3(any2());
}

现场演示

但如果引用被忽略, any1any2应该有相同的行为,对吧? 显然他们不这样做,因为f2(any1())不能用gcc或clang编译,而f2(any2())可以同时编译。

接下来的条款(第3条,特别是3.3)进一步混淆了事情:

如果A不是引用类型:[...]如果P是一个cv限定类型,则P类型的顶级cv限定符在类型推导中将被忽略。

这与关于删除引用的第2条一起似乎暗示下面的代码不应该被编译,因为含糊不清:

struct any3 {
  template <typename T> operator T&() { }
  template <typename T> operator T const&() { }
};

void f1(int) { }

int main() {
  f1(any3());
}

现场演示

然而,这对gcc和clang都可以正常工作。

我错过了什么?

编辑

我应该澄清一下,clang和gcc编译器处理这个问题的方式正是我对C ++的一般(相对高级的)理解所期待的。 一些评论者要求澄清我的困惑是什么(并且暗示,为什么我应该关心)。 我的困惑与试图理解标准的含义完全相关。 我需要清楚地了解这一点,因为我正在提交一份代码,这些代码严重依赖这项工作,并且使用它符合标准。


I'm having trouble understanding the implications of the conversion function template argument deduction rules in the C++ standard. The standard states that ([temp.deduct.conv] clause 1, §14.8.2.3.1 in N4594):

Template argument deduction is done by comparing the return type of the conversion function template (call it P) with the type that is required as the result of the conversion (call it A; see 8.5, 13.3.1.5, and 13.3.1.6 for the determination of that type) as described in 14.8.2.5.

where 14.8.2.5 ([temp.deduct.type]) is the section that describes general template argument deduction (though the most common case, function call template argument deduction [temp.deduct.call], no longer seems to point there; did it ever?). The next clause is what confuses me, though (clause 2):

If P is a reference type, the type referred to by P is used in place of P for type deduction and for any further references to or transformations of P in the remainder of this section.

To me, this seems to imply that template <class T> operator T() and template <class T> operator T&() are the same (and specifying both would result in an ambiguity). But that isn't the case in any compiler I've used! For instance:

struct any1 { template <typename T> operator T() { } };

struct any2 { template <typename T> operator T&() { } };

void f1(int) { }
void f2(int&) { }
void f3(int const&) { }

int main() {
  f1(any1());
  // f2(any1()); compile time error
  f3(any1());

  f1(any2());
  f2(any2());
  f3(any2());
}

Live Demo

But if references are ignored, any1 and any2 should have the same behavior, right? Clearly they don't, since f2(any1()) doesn't compile with either gcc or clang, while f2(any2()) compiles fine with both.

The next clause (clause 3, particularly 3.3) confuses things even further:

If A is not a reference type: [...] If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction.

This, along with clause 2 about the removal of references, would seem to imply that the following code should not compile because of an ambiguity:

struct any3 {
  template <typename T> operator T&() { }
  template <typename T> operator T const&() { }
};

void f1(int) { }

int main() {
  f1(any3());
}

Live Demo

And yet this works fine with both gcc and clang.

What am I missing?

Edit

I should clarify that the way the clang and gcc compilers handle this is exactly what I would expect from a general (relatively advanced) understanding of C++. Some commenters have asked for clarification on what my confusion is (and, implicitly, why I should care). My confusion here is entirely related to trying to understand the implications of the standard. I need a clear understanding of this because I am submitting a paper with code that relies heavily on this working and on my use of it being standards-compliant.


原文:https://stackoverflow.com/questions/39150553
更新时间:2022-06-11 09:06

最满意答案

您错误地使用了GtkDrawingArea 。 绘图区域不是画布,您只能在其上绘制一次并期望系统刷新绘图; 它是一个小部件,允许(并要求)您在expose事件期间绘制它。 当您需要处理图像时,例如通过将图像缩放到该区域,这非常有用。 如果您只需要一个显示现有图像的小部件,请改用gtk.Image

要正确使用DrawingArea ,必须从中进行子类化并处理expose事件。 例如:

import gtk, gobject

class ImageArea(gtk.DrawingArea):
    __gsignals__ = {'expose-event': 'override'}

    def __init__(self, pixbuf):
        super(ImageArea, self).__init__()
        self._pixbuf = pixbuf

    def do_expose_event(self, event):
        # clip to exposed area
        cr = self.window.cairo_create()
        cr.rectangle(tuple(event.area))
        cr.clip()
        _, _, w, h = tuple(self.allocation)

        # paint over the drawing context:
        pb = self._pixbuf
        cr.set_source_pixbuf(pb, (w - pb.get_width()) / 2, (h - pb.get_height()) / 2)
        cr.paint()

gobject.type_register(ImageArea)

You are using the GtkDrawingArea incorrectly. A drawing area is not a canvas on which you can draw only once and expect the system to refresh the drawing; it is a widget that allows (and requires) you to draw on it during the expose event. This is useful when you need to process the image, e.g. by scaling it to the area. If all you just need is a widget to show an existing image, use gtk.Image instead.

To use a DrawingArea correctly, you must subclass from it and handle the expose event. For example:

import gtk, gobject

class ImageArea(gtk.DrawingArea):
    __gsignals__ = {'expose-event': 'override'}

    def __init__(self, pixbuf):
        super(ImageArea, self).__init__()
        self._pixbuf = pixbuf

    def do_expose_event(self, event):
        # clip to exposed area
        cr = self.window.cairo_create()
        cr.rectangle(tuple(event.area))
        cr.clip()
        _, _, w, h = tuple(self.allocation)

        # paint over the drawing context:
        pb = self._pixbuf
        cr.set_source_pixbuf(pb, (w - pb.get_width()) / 2, (h - pb.get_height()) / 2)
        cr.paint()

gobject.type_register(ImageArea)

相关问答

更多
  • 您错误地使用了GtkDrawingArea 。 绘图区域不是画布,您只能在其上绘制一次并期望系统刷新绘图; 它是一个小部件,允许(并要求)您在expose事件期间绘制它。 当您需要处理图像时,例如通过将图像缩放到该区域,这非常有用。 如果您只需要一个显示现有图像的小部件,请改用gtk.Image 。 要正确使用DrawingArea ,必须从中进行子类化并处理expose事件。 例如: import gtk, gobject class ImageArea(gtk.DrawingArea): __ ...
  • 简单的解决方法是: TreeView:将Fixed Height Mode设置为No. TreeViewColumn:将“大小调整”设置为“自动”或“仅增长” 对于单列视图,列会调整大小以适合其内容,如果内容不适合,则会显示滚动条。 使用多列视图时,列将获得初始宽度,如果它们不适合,则会显示滚动条。 树视图的“固定高度模式”对于其列来说意味着“固定宽度模式”,但似乎并非如此。 或者,列的固定宽度可以超过ScrolledWindow的宽度,但这是根据需要显示滚动条。 The simple fix was: T ...
  • 删除first_draw而不是dots_queue.pop() ,只需遍历dots_queue并每次重绘所有这些。 draw函数并不意味着“我想添加一些绘图”。 相反,它是“嘿,窗口系统不知道应该在这里画什么,请填写内容”。 这就是开罗表面被清除的原因。 So while storing all actions works, it's really not ok if you are trying to have your program save your drawings, you will have ...
  • 来自Robin Dunn本人: 首先,默认情况下, Refresh()将在发送绘制事件之前擦除背景(虽然设置BG样式或捕获擦除事件会照顾到这一点。)在这种情况下,第二个也许是最明显的问题是在你的on_motion处理程序您没有通过滚动偏移来抵消ClientDC,只是缓冲区中您正在绘制线段的位置。 因此,当缓冲区被刷新到客户端DC时,它将被绘制在物理(0,0),而不是虚拟(0,0)。 换句话说,您看到的闪烁来自于在每次鼠标拖动事件后将缓冲区绘制在错误的位置,然后立即再次在Refresh()触发的on_pain ...
  • 好的...似乎我使用的配置,安装或兼容性存在问题。 我尝试了以下方法: #!/usr/bin/env ruby1.8 然后没有抛出错误,并且后续行工作: sftest = scrollframe.get_frame 现在可以将CheckButton添加到sftest然后我可以滚动查看一个Checkbutton列表... 格尔茨 GG Ok... It seems that there's a problem with my used config, installation or compatibility ...
  • 你可以添加一个伤害区域并强制重绘,我稍微修改了你的例子(抱歉我无法抗拒修复一些事情)并添加了queue_draw_area 我强烈建议避免使用Gtk.DrawingArea并使用画布小部件,在画布上绘制它会更容易,GooCanvas是一个很好的例子,但还有许多其他的可以使用。 from gi.repository import Gtk, Gdk import math, cairo class Test(Gtk.Window): def __init__(self): Gtk.W ...
  • 由于工具提示通常在用户移动鼠标时消失,您还可以绑定到EVT_MOTION而不是EVT_LEAVE_WINDOW并在用户移动鼠标时隐藏工具提示。 Robin Dunn told me: "the same thing would happen with any other widgets that are on the scrolled window, just as the frame will get a EVT_LEAVE_WINDOW when the mouse moves into the scr ...
  • 由...创建的子窗口小部件 self.label = wx.StaticText(self, -1, text, wx.DefaultPosition, wx.DefaultSize) 将self作为父级,即MainFrame类。 更改它,以便父级是滚动窗口的实例,如下所示 self.label = wx.StaticText(self.mainPanel, -1, text, wx.DefaultPosition, wx.DefaultSize) The child widgets that are ...
  • 通常,您必须绘制整个窗口小部件,并且Cairo会将绘图剪切到预定义的脏区域。 有关性能提示,请参阅“GtkWidget :: draw”信号的GTK参考手册: 信号处理程序将获得一个cr,其中一个剪辑区域已经设置到小部件的脏区域,即需要重新绘制的区域。 想要避免完全重绘自身的复杂小部件可以使用gdk_cairo_get_clip_rectangle()获取剪辑区域的完整范围,或者可以使用cairo_copy_clip_rectangle_list()获得更细粒度的脏区域表示。 因此,您可以使用gtk_wid ...
  • 第一次错过了真正的原因。 问题是您要向视口添加多个窗口小部件(只能有一个窗口小部件)。 gtk_viewport_add: assertion 'gtk_bin_get_child (bin) == NULL' failed 转换为:“视口没有子节点的断言是错误的”,因为你已经添加了一些内容。 您需要将所有容器打包到另一个容器中, 然后将其添加到视口中。 It worked with an HBox/VBox, that was the only problem there. However now tha ...

相关文章

更多

最新问答

更多
  • 您如何使用git diff文件,并将其应用于同一存储库的副本的本地分支?(How do you take a git diff file, and apply it to a local branch that is a copy of the same repository?)
  • 将长浮点值剪切为2个小数点并复制到字符数组(Cut Long Float Value to 2 decimal points and copy to Character Array)
  • OctoberCMS侧边栏不呈现(OctoberCMS Sidebar not rendering)
  • 页面加载后对象是否有资格进行垃圾回收?(Are objects eligible for garbage collection after the page loads?)
  • codeigniter中的语言不能按预期工作(language in codeigniter doesn' t work as expected)
  • 在计算机拍照在哪里进入
  • 使用cin.get()从c ++中的输入流中丢弃不需要的字符(Using cin.get() to discard unwanted characters from the input stream in c++)
  • No for循环将在for循环中运行。(No for loop will run inside for loop. Testing for primes)
  • 单页应用程序:页面重新加载(Single Page Application: page reload)
  • 在循环中选择具有相似模式的列名称(Selecting Column Name With Similar Pattern in a Loop)
  • System.StackOverflow错误(System.StackOverflow error)
  • KnockoutJS未在嵌套模板上应用beforeRemove和afterAdd(KnockoutJS not applying beforeRemove and afterAdd on nested templates)
  • 散列包括方法和/或嵌套属性(Hash include methods and/or nested attributes)
  • android - 如何避免使用Samsung RFS文件系统延迟/冻结?(android - how to avoid lag/freezes with Samsung RFS filesystem?)
  • TensorFlow:基于索引列表创建新张量(TensorFlow: Create a new tensor based on list of indices)
  • 企业安全培训的各项内容
  • 错误:RPC失败;(error: RPC failed; curl transfer closed with outstanding read data remaining)
  • C#类名中允许哪些字符?(What characters are allowed in C# class name?)
  • NumPy:将int64值存储在np.array中并使用dtype float64并将其转换回整数是否安全?(NumPy: Is it safe to store an int64 value in an np.array with dtype float64 and later convert it back to integer?)
  • 注销后如何隐藏导航portlet?(How to hide navigation portlet after logout?)
  • 将多个行和可变行移动到列(moving multiple and variable rows to columns)
  • 提交表单时忽略基础href,而不使用Javascript(ignore base href when submitting form, without using Javascript)
  • 对setOnInfoWindowClickListener的意图(Intent on setOnInfoWindowClickListener)
  • Angular $资源不会改变方法(Angular $resource doesn't change method)
  • 在Angular 5中不是一个函数(is not a function in Angular 5)
  • 如何配置Composite C1以将.m和桌面作为同一站点提供服务(How to configure Composite C1 to serve .m and desktop as the same site)
  • 不适用:悬停在悬停时:在元素之前[复制](Don't apply :hover when hovering on :before element [duplicate])
  • 常见的python rpc和cli接口(Common python rpc and cli interface)
  • Mysql DB单个字段匹配多个其他字段(Mysql DB single field matching to multiple other fields)
  • 产品页面上的Magento Up出售对齐问题(Magento Up sell alignment issue on the products page)