#include <string>
#include <iostream>
#include <type_traits>

// Forward declaration for our "default values" class.
class CQQC;

namespace detail {
    // String wrapper, convertible to std::string.
    class literal_string {
        const char* const str;
        size_t sz; // Currently not needed, but useful if additional functionality is desired.

        static constexpr size_t length(const char* const str_) {
            return *str_ ? 1 + length(str_ + 1) : 0;
        }

      public:
        template<size_t N>
        constexpr literal_string(const char (&str_)[N])
          : str(str_), sz(N - 1) { }

        constexpr literal_string(const char* const str_)
          : str(str_), sz(length(str_)) { }

        operator std::string() const {
            return std::string(str);
        }
    };

    // Generic "default values" type, specialise for each class.
    template<typename>
    struct Defaults_;

    // Defaults for CQQC.
    template<>
    struct Defaults_<::CQQC> {
        int i, j, k;
        literal_string str;

/*
        template<size_t N = 12>
        constexpr Defaults_(int i_ = 0,
                            int j_ = 42,
                            int k_ = 359,
                            const char (&str_)[N] = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
*/
    
        constexpr Defaults_(int i_ = 0,
                            int j_ = 42,
                            int k_ = 359,
                            const char* const str_ = "Hey, y'all!")
          : i(i_), j(j_), k(k_), str(str_) { }
    };
} // namespace detail

// Boilerplate macro.
#define MAKE_DEFAULTS(Class) \
    constexpr static ::detail::Defaults_<Class> Defaults = ::detail::Defaults_<Class>()
    
class CQQC {
    static_assert(std::is_literal_type<detail::Defaults_<CQQC>>::value,
                  "Default value holder isn't a literal type.");

    MAKE_DEFAULTS(CQQC);
    // Expands to:
    // constexpr static ::detail::Defaults_<CQQC> Defaults = ::detail::Defaults_<CQQC>();

    int i, j, k;
    std::string str;

  public:
    // Allows the user to specify that they want the default value.
    enum Flags { DEFAULT };

    // Initialise to defaults, unless otherwise specified.
    CQQC(int i_ = Defaults.i,
         int j_ = Defaults.j,
         int k_ = Defaults.k,
         std::string str_ = Defaults.str)
      : i(i_), j(j_), k(k_), str(str_) {}

    bool isDefault() {
        return (i   == Defaults.i &&
                j   == Defaults.j &&
                k   == Defaults.k &&
                str == Defaults.str.operator std::string());
    }

    void set_i(int i_) { i = i_; }

    // Set to default.
    void set_i(Flags f_) {
        if (f_ == Flags::DEFAULT) { i = Defaults.i; }
    }

    // And so on...
};
constexpr detail::Defaults_<CQQC> CQQC::Defaults;

int main() {
	CQQC c1(4);
	CQQC c2;
	
	if (c1.isDefault()) {
		std::cout << "c1 has default values.\n";
	} else {
		std::cout << "c1 is weird.\n";
	}
	
	if (c2.isDefault()) {
		std::cout << "c2 has default values.\n";
	} else {
		std::cout << "c2 is weird.\n";
	}

	c1.set_i(CQQC::DEFAULT);
	if (c1.isDefault()) {
		std::cout << "c1 now has default values.\n";
	} else {
		std::cout << "c1 is still weird.\n";
	}
}