我有一个问题可以最小化到以下示例
#include <iostream>
#include <string>
class A{
public:
const char* chr;
A(){chr = "aaa";}
};
class B : A{
public:
const char* chr;
B(){chr = "bbb";}
};
template <class T>
std::string to_str(T) = delete;
template<>
inline std::string
to_str<A>(A object) {
std::string str;
return str.assign((object.chr));
}
int main() {
A a;
B b;
std::cout << to_str(b) << std::endl;
}
将代码更改为std::cout << to_str(a) << std::endl;
时,代码会运行并打印&#39; aaa
&#39;,但是像这样,它会在编译和输出时停止
main.cpp: In function 'int main()':
main.cpp:30:24: error: use of deleted function 'std::__cxx11::string to_str(T) [with T = B; std::__cxx11::string = std::__cxx11::basic_string<char>]'
std::cout << to_str(b) << std::endl;
^
main.cpp:18:13: note: declared here
std::string to_str(T) = delete;
^~~~~~
exit status 1
现在可以说我有很多继承A
的课程,我可以告诉他们。编译器他们都可以转到同一个函数(一个接受A
)?
感谢。
答案 0 :(得分:5)
我可以'告诉'编译器他们都可以转到同一个函数(一个人接受A)吗?
是的,使用SFINAE和std::is_base_of
template <typename T>
typename std::enable_if<std::is_base_of<A, T>::value, std::string>::type
to_str (T const & t)
{ return t.chr; }
以下是一个完整的工作示例
#include <type_traits>
#include <iostream>
#include <string>
struct A { char const * chr; A() : chr{"aaa"} {} };
struct B : A { char const * chr; B() : chr{"bbb"} {} };
struct C { char const * chr; C() : chr{"ccc"} {} };
template <typename T>
typename std::enable_if<std::is_base_of<A, T>::value, std::string>::type
to_str (T const & t)
{ return t.chr; }
int main()
{
A a;
B b;
C c;
std::cout << to_str(a) << std::endl; // print aaa
std::cout << to_str(b) << std::endl; // print bbb
// std::cout << to_str(c) << std::endl; // compilation error
}
答案 1 :(得分:1)
我怀疑这是否真的是您问题的最小化示例。我认为发生的事情是翻译中丢失了一些重要的细节,因为你在这里展示的代码有很多问题。
B
从A
私下继承。在这种情况下,我们无法真正将B
视为A
。 如果您将继承更改为public
,那么我们可以尝试强制执行B
:
class B : public A{/*...*/};
// ...
std::cout << to_str(*static_cast<A*>(&b)) << std::endl;
但输出仍将是“aaa”,这将引导我进入下一个点
to_str
的{{1}}专业化接受值。这很重要,因为即使我们想强制A
进入,我们最终切割对象,这很重要,因为
B
重新定义B
有效隐藏 const char* chr
,由于我们已切片,因此无法恢复A::chr
是B
。
我们可以开始修复问题,但首先通过引用(或chr
引用)接受A
来修复切片,并且总是更喜欢重载而不是函数的模板特化:
const
std::string to_str(A& object) {/*...*/}
的实例中恢复B
的{{1}}。我们可以在这里采取两种方式之一。
chr
中使用A
成员,不在任何派生类中重新声明,然后派生类可以在初始化时设置它。示例:
std::string
A
中编写了一个class A{
public:
std::string chr;
A():chr{"aaa"}{}
};
class B : public A{
public:
B(){chr = "bbb";}
};
方法,派生类可以覆盖它。示例:
virtual const char* get_char()
请注意,此时我们仍然强制每个A
成为class A{
public:
const char* chr;
A(){chr = "aaa";}
virtual const char* get_chr() const{return chr;}
};
class B : public A{
public:
const char* chr;
B(){chr = "bbb";}
const char* get_chr() const override {return chr;}
};
template <class T>
std::string to_str(T) = delete;
std::string to_str(A& object) {
std::string str;
return str.assign((object.get_chr()));
// ...
std::cout << to_str(*static_cast<A*>(&b)) << std::endl;
,这会引导我进入下一点
B
将始终完全匹配您未明确专注的每种类型,在最坏的情况下是首选,并在最佳情况下引起歧义。
如果你对这个功能没有任何控制权,那么我们就会被我们所拥有的东西所困扰。但是,如果我们做,那么我们可以使用A
来接受我们需要的任何内容,以接受从template <class T> std::string to_str(T) = delete;
派生的任何内容。
通过这种方式,我们可以保留您的私人继承权,同时保留您重新声明的type_traits
成员,同时禁用A
其他所有和不要求我们{{1}我们的chr
。
示例:
to_str
总的来说,我认为最好的方法是给static_cast
一个b
每个派生类在初始化时设置,然后让你的#include <type_traits>
class A{
public:
const char* chr;
A(){chr = "aaa";}
};
class B : A{
public:
const char* chr;
B(){chr = "bbb";}
};
template<class T, class = std::enable_if_t<std::is_base_of<A, T>::value, int>>
inline std::string to_str(T& object) {
std::string str;
return str.assign((object.chr));
}
int main() {
A a;
B b;
std::cout << to_str(b) << std::endl;
}
函数专用于{{ 1}}(作为重载)打印该成员。
编辑:我忘记了最后一个音符。问题#6:A
中没有protected std::string chr
个成员。因此,您永远无法to_string
指向任何派生类的A&
指针。
答案 2 :(得分:1)
模板功能专业化不起作用。没有重载决议;它只允许用特定的模板参数替换特定的函数体。它很少有用。
你想要的是重载分辨率,可能还有标签调度。
首先完全删除它:
template <class T>
std::string to_str(T) = delete;
接下来,写一个重载:
inline std::string to_str(A object) {
std::string str;
return str.assign((object.chr));
}
并完成了。重载决议会将B
发送到A
重载。
你的下一个问题是切片。 B
有两个成员,名为chr
:A::chr
和B::chr
。没有充分的理由。此外,您不必要地复制A
(或A
的{{1}}子对象。
B
这避免了inline std::string to_str(A const& object) {
std::string str;
return str.assign((object.chr));
}
。
A
答案 3 :(得分:-3)
根据14.7.3 [temp.expl.spec]第1段,只有未删除的功能模板可以明确专门化
C++ Standard Core Language Defect Reports and Accepted Issues, Revision 97
所以,如果你改变了
template <class T>
std::string to_str(T) = delete;
例如,,
template <class T>
std::string to_str(T) { return ""; }
一切都应该有效