将函数模板移动到不同的翻译单元解决歧义错误
当我注意到将功能模板之一的定义转移到其他翻译单元时,我正在使用函数模板解决模棱两可的错误
。以下是我尝试过的两个示例。第一个示例会产生模棱两可的错误,但是当我将函数模板之一的定义移动到另一个翻译单元时,错误就消失了。
示例1
#include <iostream>
template<typename X, typename Y>
void func(X, Y)
{
std::cout<<"X-Y in order version called"<<std::endl;
}
template<typename X, typename Y>
//--------v--v----->order changed
void func(Y, X)
{
std::cout<<"Y-X in order version called"<<std::endl;
}
int main()
{
func(2,2); //this is ambiguous as expected
}
demo 表明我们会按预期遇到模棱两可的错误。
我的问题是关于下面给出的第二个示例:
示例2
main.cpp
#include <iostream>
template<typename X, typename Y>
void func(X, Y)
{
std::cout<<"X-Y in order version called"<<std::endl;
}
extern void init();
int main()
{
func(2,2); //WORKS BUT HOW? How does the compiler resolves the ambiguity here
init();
}
source2.cpp
#include <iostream>
//moved to source2.cpp
template<typename X, typename Y>
//--------v--v----->order changed
void func(Y, X)
{
std::cout<<"Y-X in order version called"<<std::endl;
}
void init()
{
func(2,2);
}
上面给出的第二个版本成功编译并产生输出:
X-Y in order version called
Y-X in order version called
我的问题是:
当我将第二个过载的定义移至其他翻译单元时,歧义性如何解决?我的意思是,我们仍然有两个插件(一个来自main.cpp中的过载,另一个来自source2.cpp中的过载),但现在我们没有遇到歧义错误。因此,C ++标准如何解决这种歧义。
C ++标准如何允许选择/首选第一个过载而不是第二个过载。我的意思是,标准中是否有参考文献,说应选择同一翻译单元中的过载的实例化。
摘要
请注意,我的第二个问题是关于为什么第一个版本比另一个版本更优于另一个翻译单元中的版本。虽然我的第一个问题是关于将定义转移到另一个翻译单元时如何消除歧义。
I was using function templates when I noticed that moving the definition of one of the function template to a different translation unit resolves the ambiguous error
. Below are the two examples that I tried. The first example produces ambiguous error as expected but when I move the definition of one of the function template into another translation unit then the error is gone.
Example 1
#include <iostream>
template<typename X, typename Y>
void func(X, Y)
{
std::cout<<"X-Y in order version called"<<std::endl;
}
template<typename X, typename Y>
//--------v--v----->order changed
void func(Y, X)
{
std::cout<<"Y-X in order version called"<<std::endl;
}
int main()
{
func(2,2); //this is ambiguous as expected
}
Demo showing that we get ambiguous error as expected.
My question is about the second example given below:
Example 2
main.cpp
#include <iostream>
template<typename X, typename Y>
void func(X, Y)
{
std::cout<<"X-Y in order version called"<<std::endl;
}
extern void init();
int main()
{
func(2,2); //WORKS BUT HOW? How does the compiler resolves the ambiguity here
init();
}
source2.cpp
#include <iostream>
//moved to source2.cpp
template<typename X, typename Y>
//--------v--v----->order changed
void func(Y, X)
{
std::cout<<"Y-X in order version called"<<std::endl;
}
void init()
{
func(2,2);
}
The second version given above successfully compiles and produces the output:
X-Y in order version called
Y-X in order version called
My questions are:
How is the ambiguity resolved when i moved the definition of the second overload to a different translation unit? I mean we still have two intantiations(one from the overload in the main.cpp and other from the overload in source2.cpp) as before but now we're not getting the ambiguity error. So how does the C++ standard resolves this ambiguity.
How does the C++ standard allows the first overload to be selected/preferred instead of the second. I mean is there a reference in the standard that says that the instantiation from the overload in the same translation unit should be selected.
Summary
Note that my second question is about why the first version is preferred over the one in the other translation unit. While my first question is about how is the ambiguity removed when moving the definition to another translation unit.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您不仅移动了定义,而且将第二个超载的唯一声明转移到了第二个翻译单元中。现在,每个翻译单元仅知道其中一个过载。
超载分辨率仅考虑可以从函数调用的上下文中通过名称查找的声明作为候选。因此,在第一个翻译单元中,只有第一个过载将作为候选人找到,在第二个翻译单元中,只能找到第二个过载。
因此,超负荷分辨率将只有一个可行的候选人可供选择。没有歧义的可能性。
超载分辨率取决于引入哪些声明并不是一个问题。仅当它违反ODR时,这才是一个问题,例如,相同
inline
函数的定义会导致导致不同的过载分辨率导致两个翻译单元的呼叫。有两个实例化,但是这些是不同函数模板的实例化,因此是不同的功能。没有理由这是一个问题。哪个模板专业化是针对哪些函数调用已经通过过载分辨率决定的。没有机会混淆它们。
You moved not only the definition, but also the only declaration of the second overload into the second translation unit. Each translation unit is now aware of only one of the overloads.
Overload resolution considers only the declarations which can be found by name lookup from the context of the function call as candidates. So in the first translation unit only the first overload will be found as candidate and in the second translation unit only the second overload can be found.
The overload resolution will therefore have only ever one viable candidate to choose from. There is no possibility for ambiguity.
It is not a problem that overload resolution depends on which declarations have been introduced. This would be an issue only if it violated ODR, e.g. because definitions for the same
inline
function make a call that results in different overload resolution results in two translation units.There are two instantiations, but these are instantiations of different function templates and therefore different functions. There is no reason for this to be an issue. Which of the template specializations is called for which function call has already been decided by overload resolution. There is no chance of confusing them.
在第二个示例中,这不是模棱两可的,因为编译器没有看到第二个翻译单元,因此仅将呼叫编译为
void func&lt; int,int&gt;(int,int)
在隐式实例化函数之后。as在第二个翻译单元中,您还会实例化void func&lt; int,int&gt;(int,int)
,但有不同的定义,它是IFNDR(不需要诊断不需要诊断) 。链接器可能会选择任何定义,因此您的程序将具有未定义的行为。In the second example, it's not ambiguous because the compiler doesn't see the second translation unit and therefore just compiles a call to
void func<int,int>(int,int)
after implicitly instantiating the function.As, in the second translation unit you're also instantiatingvoid func<int,int>(int,int)
but with a different definition, it's IFNDR (Ill-Formed No Diagnostic Required). The linker might choose any definition and therefore your program will have undefined behavior.来自 temp.over.link.link#1 :
(强调矿山)
现在,在给定的示例中,两个过载产生的专业都将具有相同的类型
void(int,int)
,并且如上所述所述,允许使用此用法。要回答第二个问题,在过载分辨率期间,呼叫
func(2,2)
infuncyin source 2.cpp 已经绑定到从第二个过载实例化版本。同样,对于call Expression
func(2,2)
内部 main.cpp ,它与第一个过载相结合到实例化版本。因此,当
init
从内部main.cpp
调用时,第二版被调用。如果我们更改了 main.cpp 内部的呼叫顺序,则输出将被颠倒,因为在过载分辨率期间,呼叫已经绑定到其各自的版本。From temp.over.link#1:
(emphasis mine)
Now, in the given example both the specializations resulting from the two overloads will have the same type
void (int, int)
and as quoted in the points above, this usage is allowed.To answer the second question, during overload resolution the call
func(2,2)
inside functioninit
in source2.cpp was already bound to the instantiated version from the second overload. Similarly, for the call expressionfunc(2,2)
inside main.cpp, it is bound to the instantiated version from the first overload.Thus, when
init
is called from insidemain.cpp
, the second version is called. If we changed the order of the calls inside main.cpp, then the output will be reversed because the call was already bound to their respective version during overload resolution.