警告:由于歧义,直接基 class 在派生中无法访问;这很严重吗?
Warning: direct base class inaccessible in derived due to ambiguity; is this serious?
我不明白这里有什么歧义。我确实确定了导致歧义的行并标记了它。
#include <string>
#include <unordered_map>
class Spell {
protected:
struct Exemplar {};
Spell() = default;
Spell (Exemplar, const std::string&);
};
class SpellFromScroll : virtual public Spell {
private:
static std::unordered_map<std::string, SpellFromScroll*> prototypesMap;
public:
static void insertInPrototypesMap (const std::string& tag, SpellFromScroll* spell) {
prototypesMap.emplace (tag, spell);
}
template <typename T> static SpellFromScroll* createFromSpell (T*);
};
std::unordered_map<std::string, SpellFromScroll*> SpellFromScroll::prototypesMap;
class SpellWithTargets : virtual public Spell {}; // *** Note: virtual
class Sleep : public SpellWithTargets {
private:
static const Sleep prototype;
public:
static std::string spellName() {return "Sleep";}
private:
Sleep (Exemplar e) : Spell (e, spellName()) {}
};
const Sleep Sleep::prototype (Exemplar{});
template <typename T>
class ScrollSpell : /*virtual*/ public T, public SpellFromScroll {};
Spell::Spell (Exemplar, const std::string& spellName) {
// Ambiguity warning!
SpellFromScroll::insertInPrototypesMap (spellName, SpellFromScroll::createFromSpell(this));
}
template <typename T>
SpellFromScroll* SpellFromScroll::createFromSpell (T*) {
return new ScrollSpell<T>;
}
int main() {}
/*
c:\ADandD>g++ -std=c++14 Ambiguity.cpp -o a.exe -Wall -Wextra -pedantic-errors
Ambiguity.cpp: In instantiation of 'class ScrollSpell<Spell>':
Ambiguity.cpp:32:13: required from 'static SpellFromScroll* SpellFromScroll::createFromSpell(T*) [with T = Spell]'
Ambiguity.cpp:27:90: required from here
Ambiguity.cpp:23:7: warning: direct base 'Spell' inaccessible in 'ScrollSpell<Spell>' due to ambiguity
class ScrollSpell : public T, public SpellFromScroll {};
^
Ambiguity.cpp:23:7: warning: virtual base 'Spell' inaccessible in 'ScrollSpell<Spell>' due to ambiguity [-Wextra]
c:\ADandD>
*/
它有多严重,随着程序的发展,以后会出现什么问题?
更新:找到一个解决方案,让T成为ScrollSpell<T>
的虚基。
但是在我的程序中T总是派生class Spell,而Spell总是T的虚基。见下图
Spell
/ \
v / \ v
/ \
/ \
SpellFromScroll SpellWithTargets
\ \
\ \
\ Sleep
\ /
\ / v
\ /
ScrollSpell<Sleep>
上图中,为什么Sleep
作为ScrollSpell<Sleep>
的虚拟基可以解决问题?
为了详细说明 Peter B 的回答,我绘制了 class T=Spell 时的图表。当 ScrollSpell 被实例化时,编译器不知道通过哪条路径到达 Scroll。但是,它也表明使用虚拟继承并不能解决歧义。这两种方法的结果都是通往 Spell
的两条路径,这是不明确的。
╔══════╗
║Spell ║
╚══════╝
/ \
╔═══════════════╗ \
║SpellFromScroll║ \
╚═══════════════╝ /
\ /
╔═══════════╗ T=Spell
║ScrollSpell║
╚═══════════╝
template <typename T>
class ScrollSpell : public T, public SpellFromScroll {};
在这里,T = Spell
,所以 ScrollSpell
class 有 Spell
class 作为一个直接的非虚拟基础 class ,也作为虚拟基地 class 到 SpellFromScroll
。这就是歧义。将基础 class T
声明为虚拟可能会解决问题。
我也不太理解设计背后的意义,所以这可能会引入一些全新的问题。
我不明白这里有什么歧义。我确实确定了导致歧义的行并标记了它。
#include <string>
#include <unordered_map>
class Spell {
protected:
struct Exemplar {};
Spell() = default;
Spell (Exemplar, const std::string&);
};
class SpellFromScroll : virtual public Spell {
private:
static std::unordered_map<std::string, SpellFromScroll*> prototypesMap;
public:
static void insertInPrototypesMap (const std::string& tag, SpellFromScroll* spell) {
prototypesMap.emplace (tag, spell);
}
template <typename T> static SpellFromScroll* createFromSpell (T*);
};
std::unordered_map<std::string, SpellFromScroll*> SpellFromScroll::prototypesMap;
class SpellWithTargets : virtual public Spell {}; // *** Note: virtual
class Sleep : public SpellWithTargets {
private:
static const Sleep prototype;
public:
static std::string spellName() {return "Sleep";}
private:
Sleep (Exemplar e) : Spell (e, spellName()) {}
};
const Sleep Sleep::prototype (Exemplar{});
template <typename T>
class ScrollSpell : /*virtual*/ public T, public SpellFromScroll {};
Spell::Spell (Exemplar, const std::string& spellName) {
// Ambiguity warning!
SpellFromScroll::insertInPrototypesMap (spellName, SpellFromScroll::createFromSpell(this));
}
template <typename T>
SpellFromScroll* SpellFromScroll::createFromSpell (T*) {
return new ScrollSpell<T>;
}
int main() {}
/*
c:\ADandD>g++ -std=c++14 Ambiguity.cpp -o a.exe -Wall -Wextra -pedantic-errors
Ambiguity.cpp: In instantiation of 'class ScrollSpell<Spell>':
Ambiguity.cpp:32:13: required from 'static SpellFromScroll* SpellFromScroll::createFromSpell(T*) [with T = Spell]'
Ambiguity.cpp:27:90: required from here
Ambiguity.cpp:23:7: warning: direct base 'Spell' inaccessible in 'ScrollSpell<Spell>' due to ambiguity
class ScrollSpell : public T, public SpellFromScroll {};
^
Ambiguity.cpp:23:7: warning: virtual base 'Spell' inaccessible in 'ScrollSpell<Spell>' due to ambiguity [-Wextra]
c:\ADandD>
*/
它有多严重,随着程序的发展,以后会出现什么问题?
更新:找到一个解决方案,让T成为ScrollSpell<T>
的虚基。
但是在我的程序中T总是派生class Spell,而Spell总是T的虚基。见下图
Spell
/ \
v / \ v
/ \
/ \
SpellFromScroll SpellWithTargets
\ \
\ \
\ Sleep
\ /
\ / v
\ /
ScrollSpell<Sleep>
上图中,为什么Sleep
作为ScrollSpell<Sleep>
的虚拟基可以解决问题?
为了详细说明 Peter B 的回答,我绘制了 class T=Spell 时的图表。当 ScrollSpell 被实例化时,编译器不知道通过哪条路径到达 Scroll。但是,它也表明使用虚拟继承并不能解决歧义。这两种方法的结果都是通往 Spell
的两条路径,这是不明确的。
╔══════╗
║Spell ║
╚══════╝
/ \
╔═══════════════╗ \
║SpellFromScroll║ \
╚═══════════════╝ /
\ /
╔═══════════╗ T=Spell
║ScrollSpell║
╚═══════════╝
template <typename T>
class ScrollSpell : public T, public SpellFromScroll {};
在这里,T = Spell
,所以 ScrollSpell
class 有 Spell
class 作为一个直接的非虚拟基础 class ,也作为虚拟基地 class 到 SpellFromScroll
。这就是歧义。将基础 class T
声明为虚拟可能会解决问题。
我也不太理解设计背后的意义,所以这可能会引入一些全新的问题。