【问题标题】:constructor of base class works but its effect gets cancelled基类的构造函数有效,但其效果被取消
【发布时间】:2026-01-04 00:15:02
【问题描述】:

我有以下代码:

item.h

using item_t = char;
    
constexpr item_t no_item = '*';
constexpr item_t item1 = 'a';
constexpr item_t item2 = 'b';
    
bool isItem(const item_t &item) {
    return item == no_item || item == item1 || item == item2;
}
        
class base {
public:
    base() : value(no_item) {}
    base(const item_t &item) : value(item) {}
    friend std::ostream &operator<<(std::ostream &out, const base &b) {
        out << b.value;
    }
    /*other stuff*/
private:
    item_t value;
};
                
template<bool(*check)(const item_t&)> 
class derivedTmp : base {
public:
    derivedTmp() : base() {}
    derivedTmp(item_t item) {
        if (!check(item)) {
            base::base();
        }
        else {
            base::base(item);
        }
    }
};

using derived = derivedTmp<isItem>;
const derived d1(item1);
const derived d2(item2);

main.cpp

#include<iostream>
#include "item.h"
    
int main() {
    cout << item1 << "\t" << item2 << "\n";
}

我真的不明白为什么它不起作用。如果有一些语法错误,它并不在意,因为这意味着我犯了一个错误,但程序编译得很好;输出是* *,而不是我期望的a b。谁能向我解释为什么?我在 Visual Studio 19 中使用 C++17。

【问题讨论】:

  • if(!check(item)) { base::base(); } - 嗯....那应该是什么? base 部分推导已经构建。你不能“重建”它。
  • 调用base的默认构造函数,否则调用其他构造函数,base部分怎么可能已经构造好了,我没有调用构造函数
  • 正如我所说,在您输入派生 ctor 的 { 时,基类构造函数已经被触发(一种或另一种方式)。你不能只是重做。我很难看到带有单个引用参数的单个构造函数以及默认引用 no_item 的单个构造函数如何无法解决您真正在这里尝试完成的任何事情。
  • @Linus “基部分怎么可能已经构造了,我没有调用构造函数” - 如果你没有从成员中显式调用基类构造函数派生类构造函数的初始化列表,编译器会为你隐式调用基类的默认构造函数。在派生构造函数的主体开始运行之前,基类总是完全构造。

标签: c++ oop inheritance c++17


【解决方案1】:
const derived d1(item1);

上面调用derivedTmp(item_t item)构造函数。

这将首先调用默认的基本构造函数base::base(),它将value 设置为no_item,即'*'。

【讨论】:

  • 对不起,你错了,deriveTmp(item_t item) 调用base::base(val) 因为!check(val) 是假的
  • 看看godbolt.org/z/dhKrje就明白了。如果您没有在派生类中调用适当的基类构造函数,则会在创建派生对象的基类组件时调用默认构造函数。
  • 当它开始执行构造函数中大括号{...} 内的内容时,默认的基本构造函数已经被触发并完成。
  • 这是否意味着我应该使用赋值运算符?
  • 这是一种解决方案。或者基类中的公共/受保护的 setter 函数,可用于重置“value”成员。
【解决方案2】:

唯一可以调用基类构造函数的地方,更不用说向它传递参数了,就是派生构造函数的member initialization list,例如:

template<bool(*check)(const item_t&)> 
class derivedTmp : public base {
public:
    derivedTmp() = default;
    derivedTmp(item_t item) : base(check(item) ? item : no_item) {}
};

您的代码不起作用的原因是因为您的两个派生构造函数都使用其无参数默认构造函数初始化基类,该构造函数将value 成员设置为no_item。在derived 构造函数的body 中调用base 构造函数不会重置 基类来更新value,就像您想的那样。如果它甚至完全编译(在我尝试的几个编译器中都没有),它会构建临时的 base 对象,这些对象只是超出范围并立即被销毁,而调用 derived 对象保持不变。

【讨论】: